mirror of
https://github.com/Crocmagnon/charasheet.git
synced 2024-11-05 06:13:55 +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>
|
<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>
|
||||||
|
|
|
@ -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"]
|
|
||||||
|
|
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 .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",
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
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.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"
|
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