Compare commits
4 commits
05871a7ddf
...
41eff3bec2
Author | SHA1 | Date | |
---|---|---|---|
Casper V. Kristensen | 41eff3bec2 | ||
Casper V. Kristensen | 0bf64182f5 | ||
Casper V. Kristensen | fad08758ce | ||
Casper V. Kristensen | b118881a13 |
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.urls import path
|
from django.urls import path, reverse_lazy
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ urlpatterns = [
|
||||||
# https://docs.djangoproject.com/en/2.2/topics/auth/default/#module-django.contrib.auth.views
|
# https://docs.djangoproject.com/en/2.2/topics/auth/default/#module-django.contrib.auth.views
|
||||||
path("login/", auth_views.LoginView.as_view(), name="login"),
|
path("login/", auth_views.LoginView.as_view(), name="login"),
|
||||||
path("logout/", auth_views.LogoutView.as_view(), name="logout"),
|
path("logout/", auth_views.LogoutView.as_view(), name="logout"),
|
||||||
path("password_change/", auth_views.PasswordChangeView.as_view(success_url="/"), name="password_change"),
|
path("password_change/", auth_views.PasswordChangeView.as_view(success_url=reverse_lazy("user_profile")), name="password_change"),
|
||||||
|
|
||||||
path("signup/", views.SignupView.as_view(), name="signup"),
|
path("signup/", views.SignupView.as_view(), name="signup"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,10 +6,8 @@ from django import forms
|
||||||
class BankImportForm(forms.Form):
|
class BankImportForm(forms.Form):
|
||||||
import_string = forms.CharField(widget=forms.Textarea)
|
import_string = forms.CharField(widget=forms.Textarea)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
helper = FormHelper()
|
||||||
super().__init__(*args, **kwargs)
|
helper.layout = Layout(
|
||||||
self.helper = FormHelper()
|
Field("import_string"),
|
||||||
self.helper.layout = Layout(
|
Submit("submit", "Import", css_class="btn btn-block btn-primary"),
|
||||||
Field("import_string"),
|
)
|
||||||
Submit("submit", "Import", css_class="btn btn-block btn-primary")
|
|
||||||
)
|
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<span class="navbar-text">Logged in as <strong>{{ user.username }}</strong></span>
|
<span class="navbar-text">Logged in as <a class="font-weight-bold" href="{% url 'user_profile' %}">{{ user.username }}</a></span>
|
||||||
<a class="btn btn-outline-danger ml-3" role="button" href="{% url 'logout' %}">Log Out</a>
|
<a class="btn btn-outline-danger ml-3" role="button" href="{% url 'logout' %}">Log Out</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-outline-info mr-1" role="button" href="{% url 'signup' %}">Sign Up</a>
|
<a class="btn btn-outline-info mr-1" role="button" href="{% url 'signup' %}">Sign Up</a>
|
||||||
|
|
63
drakul/base/templates/bootstrap4/table_inline_formset.html
Normal file
63
drakul/base/templates/bootstrap4/table_inline_formset.html
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
{% comment %}
|
||||||
|
Based on Crispy's default bootstrap4/table_inline_formset.html, but removed 'table-striped'
|
||||||
|
https://github.com/django-crispy-forms/django-crispy-forms/blob/master/crispy_forms/templates/bootstrap4/table_inline_formset.html
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load crispy_forms_utils %}
|
||||||
|
{% load crispy_forms_field %}
|
||||||
|
|
||||||
|
{% specialspaceless %}
|
||||||
|
{% if formset_tag %}
|
||||||
|
<form {{ flat_attrs|safe }} method="{{ form_method }}" {% if formset.is_multipart %} enctype="multipart/form-data"{% endif %}>
|
||||||
|
{% endif %}
|
||||||
|
{% if formset_method|lower == 'post' and not disable_csrf %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{ formset.management_form|crispy }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table{% if form_id %} id="{{ form_id }}_table"{% endif%} class="table table-sm">
|
||||||
|
<thead>
|
||||||
|
{% if formset.readonly and not formset.queryset.exists %}
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
{% for field in formset.forms.0 %}
|
||||||
|
{% if field.label and not field.is_hidden %}
|
||||||
|
<th for="{{ field.auto_id }}" class="col-form-label {% if field.field.required %}requiredField{% endif %}">
|
||||||
|
{{ field.label|safe }}{% if field.field.required and not field|is_checkbox %}<span class="asteriskField">*</span>{% endif %}
|
||||||
|
</th>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr class="d-none empty-form">
|
||||||
|
{% for field in formset.empty_form %}
|
||||||
|
{% include 'bootstrap4/field.html' with tag="td" form_show_labels=False %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for form in formset %}
|
||||||
|
{% if form_show_errors and not form.is_extra %}
|
||||||
|
{% include "bootstrap4/errors.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
{% for field in form %}
|
||||||
|
{% include 'bootstrap4/field.html' with tag="td" form_show_labels=False %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% include "bootstrap4/inputs.html" %}
|
||||||
|
|
||||||
|
{% if formset_tag %}</form>{% endif %}
|
||||||
|
{% endspecialspaceless %}
|
|
@ -31,7 +31,7 @@ class RaidResponseForm(ModelForm):
|
||||||
type="submit",
|
type="submit",
|
||||||
name="status",
|
name="status",
|
||||||
value=RaidResponse.Status.SIGNED_UP,
|
value=RaidResponse.Status.SIGNED_UP,
|
||||||
css_class=f"btn-block {'btn-success' if not is_signed_up else 'btn-primary'}"
|
css_class=f"btn btn-block {'btn-success' if not is_signed_up else 'btn-primary'}"
|
||||||
),
|
),
|
||||||
css_class="form-group col-md-3"
|
css_class="form-group col-md-3"
|
||||||
),
|
),
|
||||||
|
@ -41,7 +41,7 @@ class RaidResponseForm(ModelForm):
|
||||||
type="submit",
|
type="submit",
|
||||||
name="status",
|
name="status",
|
||||||
value=RaidResponse.Status.BACKUP,
|
value=RaidResponse.Status.BACKUP,
|
||||||
css_class="btn-warning btn-block",
|
css_class="btn btn-block btn-warning",
|
||||||
disabled=self.instance.pk and self.instance.status == RaidResponse.Status.BACKUP
|
disabled=self.instance.pk and self.instance.status == RaidResponse.Status.BACKUP
|
||||||
),
|
),
|
||||||
css_class="form-group col-md-2"
|
css_class="form-group col-md-2"
|
||||||
|
@ -52,7 +52,7 @@ class RaidResponseForm(ModelForm):
|
||||||
type="submit",
|
type="submit",
|
||||||
name="status",
|
name="status",
|
||||||
value=RaidResponse.Status.SIGNED_OFF,
|
value=RaidResponse.Status.SIGNED_OFF,
|
||||||
css_class="btn-danger btn-block",
|
css_class="btn btn-block btn-danger",
|
||||||
disabled=self.instance.pk and self.instance.status == RaidResponse.Status.SIGNED_OFF
|
disabled=self.instance.pk and self.instance.status == RaidResponse.Status.SIGNED_OFF
|
||||||
),
|
),
|
||||||
css_class="form-group col-md-2"
|
css_class="form-group col-md-2"
|
||||||
|
@ -72,35 +72,37 @@ class GuestRaidResponseForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RaidResponse
|
model = RaidResponse
|
||||||
fields = ["guest_name", "guest_klass", "role", "status"]
|
fields = ["guest_name", "guest_klass", "role", "status"]
|
||||||
|
labels = {
|
||||||
|
"guest_name": "Name",
|
||||||
|
"guest_klass": "Class"
|
||||||
|
}
|
||||||
|
|
||||||
|
helper = FormHelper()
|
||||||
|
helper.layout = Layout(
|
||||||
|
Row(
|
||||||
|
Column("guest_name", css_class="col-12 col-md-3"),
|
||||||
|
Column("guest_klass", css_class="col-12 col-md-2"),
|
||||||
|
Column("role", css_class="col-12 col-md-2"),
|
||||||
|
Column("status", css_class="col-12 col-md-3"),
|
||||||
|
Column(
|
||||||
|
HTML("<label> </label>"), # offsets the button consistent with the other input elements
|
||||||
|
Submit(
|
||||||
|
"submit",
|
||||||
|
"Add",
|
||||||
|
css_class="btn btn-block btn-primary"
|
||||||
|
),
|
||||||
|
css_class="form-group col-md-2"
|
||||||
|
),
|
||||||
|
css_class="form-row"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
helper.render_hidden_fields = True # Include MultiFormMixin's hidden form_id_field_prefix
|
||||||
|
helper.form_tag = False
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.fields["status"].initial = RaidResponse.Status.SIGNED_UP
|
self.fields["status"].initial = RaidResponse.Status.SIGNED_UP
|
||||||
|
|
||||||
self.helper = FormHelper()
|
|
||||||
self.helper.layout = Layout(
|
|
||||||
Row(
|
|
||||||
Column("guest_name", css_class="col-12 col-md-3"),
|
|
||||||
Column("guest_klass", css_class="col-12 col-md-2"),
|
|
||||||
Column("role", css_class="col-12 col-md-2"),
|
|
||||||
Column("status", css_class="col-12 col-md-3"),
|
|
||||||
Column(
|
|
||||||
HTML("<label> </label>"), # offsets the button consistent with the other input elements
|
|
||||||
StrictButton(
|
|
||||||
"Add",
|
|
||||||
type="submit",
|
|
||||||
css_class=f"btn-block btn-primary"
|
|
||||||
),
|
|
||||||
css_class="form-group col-md-2"
|
|
||||||
),
|
|
||||||
css_class="form-row"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.helper.render_hidden_fields = True # Include MultiFormMixin's hidden form_id_field_prefix
|
|
||||||
self.helper.form_show_labels = True
|
|
||||||
self.helper.form_tag = False
|
|
||||||
|
|
||||||
|
|
||||||
class RaidResponseChangeForm(ModelForm):
|
class RaidResponseChangeForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -118,8 +120,8 @@ class RaidForm(ModelForm):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.helper = FormHelper()
|
|
||||||
|
|
||||||
|
self.helper = FormHelper()
|
||||||
if self.instance.pk is None:
|
if self.instance.pk is None:
|
||||||
submit_button = Submit("submit", "Create", css_class="btn btn-success float-right")
|
submit_button = Submit("submit", "Create", css_class="btn btn-success float-right")
|
||||||
else:
|
else:
|
||||||
|
@ -134,16 +136,16 @@ class RaidForm(ModelForm):
|
||||||
),
|
),
|
||||||
submit_button
|
submit_button
|
||||||
)
|
)
|
||||||
self.helper.render_hidden_fields = True
|
self.helper.render_hidden_fields = True # Include MultiFormMixin's hidden form_id_field_prefix
|
||||||
|
|
||||||
|
|
||||||
class RaidCommentForm(ModelForm):
|
class RaidCommentForm(ModelForm):
|
||||||
helper = FormHelper()
|
helper = FormHelper()
|
||||||
helper.layout = Layout(
|
helper.layout = Layout(
|
||||||
Field("body", label="lol"),
|
Field("body"),
|
||||||
Submit("submit", "Submit"),
|
Submit("submit", "Submit", css_class="btn btn-primary"),
|
||||||
)
|
)
|
||||||
helper.render_hidden_fields = True
|
helper.render_hidden_fields = True # Include MultiFormMixin's hidden form_id_field_prefix
|
||||||
helper.form_show_labels = False
|
helper.form_show_labels = False
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -163,16 +165,14 @@ RaidResponseFormSet = inlineformset_factory(
|
||||||
class RaidResponseFormSetHelper(FormHelper):
|
class RaidResponseFormSetHelper(FormHelper):
|
||||||
template = "raids/raid_response_table_inline_formset.html"
|
template = "raids/raid_response_table_inline_formset.html"
|
||||||
|
|
||||||
def __init__(self, form=None):
|
layout = Layout(
|
||||||
super().__init__(form)
|
Field("character", css_class="character-select"),
|
||||||
self.layout = Layout(
|
Field("guest_name", css_class="guest-name"),
|
||||||
Field("character", css_class="character-select"),
|
Field("guest_klass", css_class="guest-klass"),
|
||||||
Field("guest_name", css_class="guest-name"),
|
Field("role", css_class="role-select"),
|
||||||
Field("guest_klass", css_class="guest-klass"),
|
Field("status", css_class="status-select"),
|
||||||
Field("role", css_class="role-select"),
|
Field("group", css_class="group-select"),
|
||||||
Field("status", css_class="status-select"),
|
Field("note"),
|
||||||
Field("group", css_class="group-select"),
|
Field("attendance", css_class="attendance-input", style="width: 10ch"),
|
||||||
Field("note"),
|
)
|
||||||
Field("attendance", css_class="attendance-input", style="width: 10ch"),
|
form_tag = False
|
||||||
)
|
|
||||||
self.form_tag = False
|
|
||||||
|
|
|
@ -209,13 +209,13 @@ class RaidChangeView(PermissionRequiredMixin, SingleObjectMixin, MultiModelFormV
|
||||||
def get_raid_response_formset_instance(self):
|
def get_raid_response_formset_instance(self):
|
||||||
return self.object
|
return self.object
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("raid_detail", kwargs={"pk": self.object.pk})
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs["raid_response_formset_helper"] = RaidResponseFormSetHelper()
|
kwargs["raid_response_formset_helper"] = RaidResponseFormSetHelper()
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("raid_detail", kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object()
|
self.object = self.get_object()
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
|
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
|
||||||
|
|
||||||
|
from .forms import UserCharactersInlineFormSet
|
||||||
from .models import User, Character, Rank
|
from .models import User, Character, Rank
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ class RankAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
class CharacterInline(admin.TabularInline):
|
class CharacterInline(admin.TabularInline):
|
||||||
model = Character
|
model = Character
|
||||||
|
formset = UserCharactersInlineFormSet # disallows deleting user's main character
|
||||||
fields = ["name", "klass", "role"]
|
fields = ["name", "klass", "role"]
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
from django.forms import ModelForm
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import Layout, Field
|
||||||
|
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
|
||||||
|
from django.forms import ModelForm, inlineformset_factory, BaseInlineFormSet
|
||||||
|
|
||||||
from .models import Character
|
from .models import Character
|
||||||
|
from ..users.models import User
|
||||||
|
|
||||||
|
|
||||||
class CharacterForm(ModelForm):
|
class CharacterForm(ModelForm):
|
||||||
|
@ -8,3 +12,51 @@ class CharacterForm(ModelForm):
|
||||||
model = Character
|
model = Character
|
||||||
fields = ["name", "klass", "role"]
|
fields = ["name", "klass", "role"]
|
||||||
|
|
||||||
|
|
||||||
|
class UserForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ["username", "rank", "main"]
|
||||||
|
|
||||||
|
def __init__(self, user, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields["rank"].disabled = True
|
||||||
|
self.fields["main"].queryset = user.characters
|
||||||
|
|
||||||
|
helper = FormHelper()
|
||||||
|
helper.layout = Layout(
|
||||||
|
Field("username"),
|
||||||
|
Field("rank"),
|
||||||
|
Field("main"),
|
||||||
|
)
|
||||||
|
helper.render_hidden_fields = True # Include MultiFormMixin's hidden form_id_field_prefix
|
||||||
|
helper.form_tag = False
|
||||||
|
|
||||||
|
|
||||||
|
class UserCharactersInlineFormSet(BaseInlineFormSet):
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
for form in self.deleted_forms:
|
||||||
|
if form.instance == self.instance.main:
|
||||||
|
form._errors[NON_FIELD_ERRORS] = self.error_class(["Can't delete user's main character."])
|
||||||
|
raise ValidationError(["Can't delete user's main character."])
|
||||||
|
|
||||||
|
|
||||||
|
UserCharactersFormSet = inlineformset_factory(
|
||||||
|
User, # parent model
|
||||||
|
Character,
|
||||||
|
formset=UserCharactersInlineFormSet,
|
||||||
|
fields=["name", "klass", "role"],
|
||||||
|
can_delete=True,
|
||||||
|
extra=1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UserCharactersFormSetHelper(FormHelper):
|
||||||
|
template = "bootstrap4/table_inline_formset.html"
|
||||||
|
layout = Layout(
|
||||||
|
Field("name"),
|
||||||
|
Field("klass"),
|
||||||
|
Field("role")
|
||||||
|
)
|
||||||
|
form_tag = False
|
||||||
|
|
19
drakul/users/migrations/0006_auto_20200924_2019.py
Normal file
19
drakul/users/migrations/0006_auto_20200924_2019.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.1.1 on 2020-09-24 20:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0005_auto_20191121_1646'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='main',
|
||||||
|
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='users.character'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -33,8 +33,7 @@ class User(AbstractUser):
|
||||||
main = models.OneToOneField(
|
main = models.OneToOneField(
|
||||||
"Character",
|
"Character",
|
||||||
related_name="+",
|
related_name="+",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE # has to cascade to allow User deletion, since 'main' Character is circularly related
|
||||||
blank=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
rank = models.ForeignKey(
|
rank = models.ForeignKey(
|
||||||
|
@ -124,5 +123,12 @@ class Character(models.Model):
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.name = self.name.capitalize()
|
self.name = self.name.capitalize()
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
if self.user.main == self:
|
||||||
|
# Sanity check: this is already checked by the profile and admin forms which allow character deletion, but
|
||||||
|
# we check it again to avoid unintentionally cascading the delete to the entire user.
|
||||||
|
raise ValidationError("Can't delete user's main character.")
|
||||||
|
return super().delete(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
30
drakul/users/templates/users/profile.html
Normal file
30
drakul/users/templates/users/profile.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block title %}Profile{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="card mb-3">
|
||||||
|
<h5 class="card-header">Profile</h5>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post" action="{% url 'user_profile' %}">
|
||||||
|
{% crispy user_form %}
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<a class="btn btn-outline-info float-right mr-2" role="button" href="{% url 'password_change' %}">Change Password</a>
|
||||||
|
<input class="btn btn-primary float-right" type="submit" name="submit" value="Save">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="my-4"></div>
|
||||||
|
<h5>Characters</h5>
|
||||||
|
<hr>
|
||||||
|
<form method="post" class="table-borderless" action="{% url 'user_profile' %}">
|
||||||
|
{% crispy user_characters_formset user_characters_formset_helper %}
|
||||||
|
<input class="btn btn-primary float-right" type="submit" name="submit" value="Save">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -4,4 +4,5 @@ from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("attendance/", views.AttendanceView.as_view(), name="attendance"),
|
path("attendance/", views.AttendanceView.as_view(), name="attendance"),
|
||||||
|
path("profile/", views.UserProfileView.as_view(), name="user_profile"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.db.models import Avg, Q, Value
|
from django.db.models import Avg, Q, Value
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
|
from .forms import UserForm, UserCharactersFormSet, UserCharactersFormSetHelper
|
||||||
from .models import User
|
from .models import User
|
||||||
|
from ..base.views import MultiModelFormView
|
||||||
from ..raids.models import Raid, RaidResponse
|
from ..raids.models import Raid, RaidResponse
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,6 +52,37 @@ class AttendanceView(TemplateView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
# CharacterDetailView:
|
class UserProfileView(LoginRequiredMixin, SingleObjectMixin, MultiModelFormView):
|
||||||
#slug_field = "title"
|
template_name = "users/profile.html"
|
||||||
#slug_url_kwarg = "title"
|
form_classes = {
|
||||||
|
"user_form": UserForm,
|
||||||
|
"user_characters_formset": UserCharactersFormSet
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_object(self, queryset=None):
|
||||||
|
return self.request.user
|
||||||
|
|
||||||
|
def get_user_form_instance(self):
|
||||||
|
return self.object
|
||||||
|
|
||||||
|
def get_user_characters_formset_instance(self):
|
||||||
|
return self.object
|
||||||
|
|
||||||
|
def get_user_form_kwargs(self, kwargs):
|
||||||
|
kwargs["user"] = self.request.user
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs["user_characters_formset_helper"] = UserCharactersFormSetHelper()
|
||||||
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse("user_profile")
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
Loading…
Reference in a new issue