mirror of
https://github.com/Crocmagnon/charasheet.git
synced 2024-12-22 13:51:49 +01:00
Move parties to separate app
This commit is contained in:
parent
7aeb983f99
commit
bb55490a91
23 changed files with 268 additions and 13 deletions
|
@ -29,6 +29,7 @@
|
|||
<list>
|
||||
<option value="$MODULE_DIR$/src/common/templates" />
|
||||
<option value="$MODULE_DIR$/src/character/templates" />
|
||||
<option value="$MODULE_DIR$/src/party/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
|
|
|
@ -210,9 +210,3 @@ class WeaponAdmin(admin.ModelAdmin):
|
|||
class HarmfulStateAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "description"]
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
@admin.register(models.Party)
|
||||
class PartyAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "game_master"]
|
||||
search_fields = ["name"]
|
||||
|
|
19
src/character/migrations/0036_delete_party.py
Normal file
19
src/character/migrations/0036_delete_party.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 4.1.2 on 2022-11-02 22:28
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"character",
|
||||
"0035_alter_capability_options_alter_harmfulstate_options_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name="Party",
|
||||
),
|
||||
]
|
|
@ -1 +1 @@
|
|||
0035_alter_capability_options_alter_harmfulstate_options_and_more
|
||||
0036_delete_party
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from .capabilities import Capability, Path, RacialCapability
|
||||
from .character import Character, HarmfulState, Profile, Race
|
||||
from .equipment import Weapon
|
||||
from .party import Party
|
||||
|
||||
__all__ = [
|
||||
"Capability",
|
||||
|
@ -12,5 +11,4 @@ __all__ = [
|
|||
"Profile",
|
||||
"Race",
|
||||
"Weapon",
|
||||
"Party",
|
||||
]
|
||||
|
|
|
@ -75,6 +75,7 @@ CUSTOM_APPS = [
|
|||
"whitenoise.runserver_nostatic", # should be first
|
||||
"common",
|
||||
"character",
|
||||
"party",
|
||||
]
|
||||
|
||||
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS
|
||||
|
|
|
@ -48,6 +48,7 @@ urlpatterns = [
|
|||
path("admin/", admin.site.urls),
|
||||
path("", hello_world, name="hello_world"),
|
||||
path("character/", include("character.urls", namespace="character")),
|
||||
path("party/", include("party.urls", namespace="party")),
|
||||
]
|
||||
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
@ -7,16 +7,21 @@
|
|||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="{% url "character:list" %}">Mes perso</a>
|
||||
<a class="nav-link" href="{% url "character:list" %}">Mes perso</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="{% url "character:list" %}">Mes groupes</a>
|
||||
<a class="nav-link" href="{% url "party:list" %}">Mes groupes</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
{% if user.is_staff %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url "admin:index" %}">Admin</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="{% url "logout" %}">Déconnexion</a>
|
||||
<a class="nav-link" href="{% url "logout" %}">Déconnexion</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
|
0
src/party/__init__.py
Normal file
0
src/party/__init__.py
Normal file
9
src/party/admin.py
Normal file
9
src/party/admin.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from party import models
|
||||
|
||||
|
||||
@admin.register(models.Party)
|
||||
class PartyAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "game_master"]
|
||||
search_fields = ["name"]
|
6
src/party/apps.py
Normal file
6
src/party/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PartyConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "party"
|
9
src/party/forms.py
Normal file
9
src/party/forms.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django import forms
|
||||
|
||||
from party.models import Party
|
||||
|
||||
|
||||
class PartyForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Party
|
||||
fields = ["name", "characters"]
|
74
src/party/migrations/0001_initial.py
Normal file
74
src/party/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Generated by Django 4.1.2 on 2022-11-02 22:28
|
||||
|
||||
import django.db.models.deletion
|
||||
import django_extensions.db.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("character", "0036_delete_party"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Party",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"name",
|
||||
models.CharField(max_length=100, unique=True, verbose_name="nom"),
|
||||
),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
(
|
||||
"characters",
|
||||
models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="parties",
|
||||
to="character.character",
|
||||
verbose_name="personnages",
|
||||
),
|
||||
),
|
||||
(
|
||||
"game_master",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="parties",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="meneur de jeu",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Groupe",
|
||||
"verbose_name_plural": "Groupes",
|
||||
"ordering": ["name"],
|
||||
"get_latest_by": "modified",
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
0
src/party/migrations/__init__.py
Normal file
0
src/party/migrations/__init__.py
Normal file
1
src/party/migrations/max_migration.txt
Normal file
1
src/party/migrations/max_migration.txt
Normal file
|
@ -0,0 +1 @@
|
|||
0001_initial
|
|
@ -1,7 +1,20 @@
|
|||
from django.db import models
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
|
||||
from common.models import UniquelyNamedModel
|
||||
from character.models import Character
|
||||
from common.models import UniquelyNamedModel, UniquelyNamedModelManager
|
||||
|
||||
|
||||
class PartyQuerySet(models.QuerySet):
|
||||
def managed_by(self, user):
|
||||
return self.filter(game_master=user)
|
||||
|
||||
def played_by(self, user):
|
||||
return self.filter(characters__in=Character.objects.filter(player=user))
|
||||
|
||||
|
||||
class PartyManager(UniquelyNamedModelManager):
|
||||
pass
|
||||
|
||||
|
||||
class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
|
@ -18,6 +31,8 @@ class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
|
|||
verbose_name="personnages",
|
||||
)
|
||||
|
||||
objects = PartyManager.from_queryset(PartyQuerySet)()
|
||||
|
||||
class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta):
|
||||
verbose_name = "Groupe"
|
||||
verbose_name_plural = "Groupes"
|
27
src/party/templates/party/parties_list.html
Normal file
27
src/party/templates/party/parties_list.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "common/base.html" %}
|
||||
{% load character_extras %}
|
||||
|
||||
{% block title %}Groupes{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Groupes</h1>
|
||||
<a href="{% url "party:create" %}" class="btn btn-primary mb-3">
|
||||
<i class="fa-solid fa-user-plus"></i> Nouveau groupe
|
||||
</a>
|
||||
<h2>Groupes que vous gérez</h2>
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
|
||||
{% for party in managed_parties %}
|
||||
{% include "party/snippets/party_card.html" %}
|
||||
{% empty %}
|
||||
<div class="col">Aucun</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<h2 class="mt-3">Groupes dont vous êtes membre</h2>
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
|
||||
{% for party in played_parties %}
|
||||
{% include "party/snippets/party_card.html" %}
|
||||
{% empty %}
|
||||
<div class="col">Aucun</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
14
src/party/templates/party/party_create.html
Normal file
14
src/party/templates/party/party_create.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% extends "common/base.html" %}
|
||||
{% load django_bootstrap5 %}
|
||||
{% load character_extras %}
|
||||
|
||||
{% block title %}Créer groupe{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Créer un groupe</h1>
|
||||
<form action="{% url "party:create" %}" method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% bootstrap_button type="submit" content="Créer" %}
|
||||
</form>
|
||||
{% endblock %}
|
12
src/party/templates/party/snippets/party_card.html
Normal file
12
src/party/templates/party/snippets/party_card.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="col">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
{{ party.name }}
|
||||
</h5>
|
||||
<a href="" class="btn btn-sm btn-success">
|
||||
<i class="fa-solid fa-users"></i> Accéder
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
0
src/party/tests/__init__.py
Normal file
0
src/party/tests/__init__.py
Normal file
30
src/party/tests/test_models.py
Normal file
30
src/party/tests/test_models.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from model_bakery import baker
|
||||
|
||||
from character.models import Character
|
||||
from common.models import User
|
||||
from party.models import Party
|
||||
|
||||
|
||||
def test_party_managed_by(db):
|
||||
game_master = User.objects.create_user("game_master")
|
||||
other_user = User.objects.create_user("other")
|
||||
expected = Party.objects.create(name="some name", game_master=game_master)
|
||||
Party.objects.create(name="some other name", game_master=other_user)
|
||||
related_to = Party.objects.managed_by(game_master)
|
||||
assert len(related_to) == 1
|
||||
assert related_to[0] == expected
|
||||
|
||||
|
||||
def test_party_played_by(db):
|
||||
player = User.objects.create_user("game_master")
|
||||
player_character = baker.make(Character, player=player)
|
||||
other_user = User.objects.create_user("other")
|
||||
other_character = baker.make(Character, player=other_user)
|
||||
|
||||
expected = Party.objects.create(name="some name", game_master=other_user)
|
||||
expected.characters.add(player_character)
|
||||
other_party = Party.objects.create(name="some other name", game_master=other_user)
|
||||
other_party.characters.add(other_character)
|
||||
related_to = Party.objects.played_by(player)
|
||||
assert len(related_to) == 1
|
||||
assert related_to[0] == expected
|
9
src/party/urls.py
Normal file
9
src/party/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.urls import path
|
||||
|
||||
from party import views
|
||||
|
||||
app_name = "party"
|
||||
urlpatterns = [
|
||||
path("", views.parties_list, name="list"),
|
||||
path("create/", views.party_create, name="create"),
|
||||
]
|
30
src/party/views.py
Normal file
30
src/party/views.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
from party.forms import PartyForm
|
||||
from party.models import Party
|
||||
|
||||
|
||||
@login_required
|
||||
def parties_list(request):
|
||||
context = {
|
||||
"managed_parties": Party.objects.managed_by(request.user),
|
||||
"played_parties": Party.objects.played_by(request.user),
|
||||
}
|
||||
return render(request, "party/parties_list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def party_create(request):
|
||||
if request.method == "GET":
|
||||
form = PartyForm()
|
||||
else:
|
||||
form = PartyForm(request.POST)
|
||||
if form.is_valid():
|
||||
party = form.save(commit=False)
|
||||
party.game_master = request.user
|
||||
party.save()
|
||||
form.save_m2m()
|
||||
return redirect("party:list")
|
||||
context = {"form": form}
|
||||
return render(request, "party/party_create.html", context)
|
Loading…
Reference in a new issue