Move parties to separate app

This commit is contained in:
Gabriel Augendre 2022-11-02 23:52:28 +01:00
parent 7aeb983f99
commit bb55490a91
23 changed files with 268 additions and 13 deletions

View file

@ -29,6 +29,7 @@
<list> <list>
<option value="$MODULE_DIR$/src/common/templates" /> <option value="$MODULE_DIR$/src/common/templates" />
<option value="$MODULE_DIR$/src/character/templates" /> <option value="$MODULE_DIR$/src/character/templates" />
<option value="$MODULE_DIR$/src/party/templates" />
</list> </list>
</option> </option>
</component> </component>

View file

@ -210,9 +210,3 @@ class WeaponAdmin(admin.ModelAdmin):
class HarmfulStateAdmin(admin.ModelAdmin): class HarmfulStateAdmin(admin.ModelAdmin):
list_display = ["name", "description"] list_display = ["name", "description"]
search_fields = ["name"] search_fields = ["name"]
@admin.register(models.Party)
class PartyAdmin(admin.ModelAdmin):
list_display = ["name", "game_master"]
search_fields = ["name"]

View 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",
),
]

View file

@ -1 +1 @@
0035_alter_capability_options_alter_harmfulstate_options_and_more 0036_delete_party

View file

@ -1,7 +1,6 @@
from .capabilities import Capability, Path, RacialCapability from .capabilities import Capability, Path, RacialCapability
from .character import Character, HarmfulState, Profile, Race from .character import Character, HarmfulState, Profile, Race
from .equipment import Weapon from .equipment import Weapon
from .party import Party
__all__ = [ __all__ = [
"Capability", "Capability",
@ -12,5 +11,4 @@ __all__ = [
"Profile", "Profile",
"Race", "Race",
"Weapon", "Weapon",
"Party",
] ]

View file

@ -75,6 +75,7 @@ CUSTOM_APPS = [
"whitenoise.runserver_nostatic", # should be first "whitenoise.runserver_nostatic", # should be first
"common", "common",
"character", "character",
"party",
] ]
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS

View file

@ -48,6 +48,7 @@ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("", hello_world, name="hello_world"), path("", hello_world, name="hello_world"),
path("character/", include("character.urls", namespace="character")), path("character/", include("character.urls", namespace="character")),
path("party/", include("party.urls", namespace="party")),
] ]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -7,16 +7,21 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> <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>
<li class="nav-item"> <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> </li>
</ul> </ul>
<ul class="navbar-nav"> <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 %} {% if user.is_authenticated %}
<li class="nav-item"> <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> </li>
{% endif %} {% endif %}
</ul> </ul>

0
src/party/__init__.py Normal file
View file

9
src/party/admin.py Normal file
View 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
View 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
View 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"]

View 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,
},
),
]

View file

View file

@ -0,0 +1 @@
0001_initial

View file

@ -1,7 +1,20 @@
from django.db import models from django.db import models
from django_extensions.db.models import TimeStampedModel 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): class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
@ -18,6 +31,8 @@ class Party(UniquelyNamedModel, TimeStampedModel, models.Model):
verbose_name="personnages", verbose_name="personnages",
) )
objects = PartyManager.from_queryset(PartyQuerySet)()
class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta): class Meta(UniquelyNamedModel.Meta, TimeStampedModel.Meta):
verbose_name = "Groupe" verbose_name = "Groupe"
verbose_name_plural = "Groupes" verbose_name_plural = "Groupes"

View 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 %}

View 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 %}

View 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>

View file

View 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
View 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
View 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)