Makes forms more generic and implements a set of class-based CRUD views for character objects.
This commit is contained in:
parent
34915980c4
commit
712177b718
5 changed files with 222 additions and 165 deletions
|
|
@ -1,10 +1,20 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.forms import UserCreationForm, UsernameField
|
from django.contrib.auth.forms import UserCreationForm, UsernameField
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from django.utils.html import escape
|
||||||
from evennia.utils import class_from_module
|
from evennia.utils import class_from_module
|
||||||
from random import choice, randint
|
|
||||||
|
|
||||||
class AccountForm(UserCreationForm):
|
class EvenniaForm(forms.Form):
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned = super(EvenniaForm, self).clean()
|
||||||
|
|
||||||
|
# Escape all values provided by user
|
||||||
|
cleaned = {k:escape(v) for k,v in cleaned.items()}
|
||||||
|
return cleaned
|
||||||
|
|
||||||
|
class AccountForm(EvenniaForm, UserCreationForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||||
|
|
@ -13,130 +23,34 @@ class AccountForm(UserCreationForm):
|
||||||
|
|
||||||
email = forms.EmailField(help_text="A valid email address. Optional; used for password resets.", required=False)
|
email = forms.EmailField(help_text="A valid email address. Optional; used for password resets.", required=False)
|
||||||
|
|
||||||
class CharacterForm(forms.Form):
|
class ObjectForm(EvenniaForm, ModelForm):
|
||||||
name = forms.CharField(help_text="The name of your intended character.")
|
|
||||||
age = forms.IntegerField(min_value=3, max_value=99, help_text="How old your character should be once spawned.")
|
|
||||||
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}), max_length=2048, min_length=160, required=False)
|
|
||||||
|
|
||||||
@classmethod
|
class Meta:
|
||||||
def assign_attributes(cls, attribute_list, points, min_points, max_points):
|
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
|
||||||
"""
|
fields = ("db_key",)
|
||||||
Randomly distributes a number of points across the given attributes,
|
labels = {
|
||||||
while also ensuring each attribute gets at least a certain amount
|
'db_key': 'Name',
|
||||||
and at most a certain amount.
|
}
|
||||||
|
|
||||||
|
class CharacterForm(ObjectForm):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
# Get the correct object model
|
||||||
|
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
|
||||||
|
# Allow entry of the 'key' field
|
||||||
|
fields = ("db_key",)
|
||||||
|
# Rename 'key' to something more intelligible
|
||||||
|
labels = {
|
||||||
|
'db_key': 'Name',
|
||||||
|
}
|
||||||
|
|
||||||
Args:
|
# Fields pertaining to user-configurable attributes on the Character object.
|
||||||
attribute_list (iterable): List or tuple of attribute names to assign
|
desc = forms.CharField(label='Description', widget=forms.Textarea(attrs={'rows': 3}), max_length=2048, required=False)
|
||||||
points to.
|
|
||||||
points (int): Starting number of points
|
|
||||||
min_points (int): Least amount of points each attribute should have
|
|
||||||
max_points (int): Most amount of points each attribute should have
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
spread (dict): Dict of attributes and a point assignment.
|
|
||||||
|
|
||||||
"""
|
|
||||||
num_buckets = len(attribute_list)
|
|
||||||
point_spread = (x for x in self.random_distribution(points, num_buckets, min_points, max_points))
|
|
||||||
|
|
||||||
# For each field, get the point calculation for the next attribute value generated
|
|
||||||
return {attribute: next(point_spread) for k in attribute_list}
|
|
||||||
|
|
||||||
@classmethod
|
class CharacterUpdateForm(CharacterForm):
|
||||||
def random_distribution(cls, points, num_buckets, min_points, max_points):
|
"""
|
||||||
"""
|
Provides a form that only allows updating of db attributes, not model
|
||||||
Distributes a set number of points randomly across a number of 'buckets'
|
attributes.
|
||||||
while also attempting to ensure each bucket's value finishes within a
|
|
||||||
certain range.
|
|
||||||
|
|
||||||
If your math doesn't add up (you try to distribute 5 points across 100
|
|
||||||
buckets and insist each bucket has at least 20 points), the algorithm
|
|
||||||
will return the best spread it could achieve but will not raise an error
|
|
||||||
(so in this case, 5 random buckets would get 1 point each and that's all).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
points (int): The number of points to distribute.
|
|
||||||
num_buckets (int): The number of 'buckets' (or stats, skills, etc)
|
|
||||||
you wish to distribute points to.
|
|
||||||
min_points (int): The least amount of points each bucket should have.
|
|
||||||
max_points (int): The most points each bucket should have.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
buckets (list): List of random point assignments.
|
|
||||||
|
|
||||||
"""
|
|
||||||
buckets = [0 for x in range(num_buckets)]
|
|
||||||
indices = [i for (i, value) in enumerate(buckets)]
|
|
||||||
|
|
||||||
# Do this while we have eligible buckets, points to assign and we haven't
|
"""
|
||||||
# maxed out all the buckets.
|
pass
|
||||||
while indices and points and sum(buckets) <= (max_points * num_buckets):
|
|
||||||
# Pick a random bucket index
|
|
||||||
index = choice(indices)
|
|
||||||
|
|
||||||
# Add to bucket
|
|
||||||
buckets[index] = buckets[index] + 1
|
|
||||||
points = points - 1
|
|
||||||
|
|
||||||
# Get the indices of eligible buckets
|
|
||||||
indices = [i for (i, value) in enumerate(buckets) if (value < min_points) or (value < max_points)]
|
|
||||||
|
|
||||||
return buckets
|
|
||||||
|
|
||||||
class ExtendedCharacterForm(CharacterForm):
|
|
||||||
|
|
||||||
GENDERS = (
|
|
||||||
('male', 'Male'),
|
|
||||||
('female', 'Female'),
|
|
||||||
('androgynous', 'Androgynous'),
|
|
||||||
('special', 'Special')
|
|
||||||
)
|
|
||||||
|
|
||||||
RACES = (
|
|
||||||
('human', 'Human'),
|
|
||||||
('elf', 'Elf'),
|
|
||||||
('orc', 'Orc'),
|
|
||||||
)
|
|
||||||
|
|
||||||
CLASSES = (
|
|
||||||
('civilian', 'Civilian'),
|
|
||||||
('warrior', 'Warrior'),
|
|
||||||
('thief', 'Thief'),
|
|
||||||
('cleric', 'Cleric')
|
|
||||||
)
|
|
||||||
|
|
||||||
PERKS = (
|
|
||||||
('strong', 'Extra strength'),
|
|
||||||
('nimble', 'Quick on their toes'),
|
|
||||||
('diplomatic', 'Fast talker')
|
|
||||||
)
|
|
||||||
|
|
||||||
name = forms.CharField(help_text="The name of your intended character.")
|
|
||||||
age = forms.IntegerField(min_value=3, max_value=99, help_text="How old your character should be once spawned.")
|
|
||||||
gender = forms.ChoiceField(choices=GENDERS, help_text="Which end of the multidimensional spectrum does your character most closely align with, in terms of gender?")
|
|
||||||
race = forms.ChoiceField(choices=RACES, help_text="What race does your character belong to?")
|
|
||||||
job = forms.ChoiceField(choices=CLASSES, help_text="What profession or role does your character fulfill or is otherwise destined to?")
|
|
||||||
|
|
||||||
perks = forms.MultipleChoiceField(choices=PERKS, help_text="What extraordinary abilities does your character possess?")
|
|
||||||
description = forms.CharField(widget=forms.Textarea(attrs={'rows': 3}), max_length=2048, min_length=160, required=False)
|
|
||||||
|
|
||||||
strength = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
perception = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
intelligence = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
dexterity = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
charisma = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
vitality = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
magic = forms.IntegerField(min_value=1, max_value=10)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
# Do all the normal initizliation stuff that would otherwise be happening
|
|
||||||
super(ExtendedCharacterCreationForm, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Given a pool of points, let's randomly distribute them across attributes.
|
|
||||||
# First get a list of attributes
|
|
||||||
attributes = ('strength', 'perception', 'intelligence', 'dexterity', 'charisma', 'vitality', 'magic')
|
|
||||||
# Distribute a random number of points across them
|
|
||||||
attrs = self.assign_attributes(attributes, 50, 1, 10)
|
|
||||||
# Initialize the form with the results of the point distribution
|
|
||||||
for field in attrs.keys():
|
|
||||||
self.initial[field] = attrs[field]
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block titleblock %}
|
{% block titleblock %}
|
||||||
Character Creation
|
{{ view.page_title }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
@ -12,7 +12,7 @@ Character Creation
|
||||||
<div class="col-lg-12 col-sm-12">
|
<div class="col-lg-12 col-sm-12">
|
||||||
<div class="card mt-3">
|
<div class="card mt-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h1 class="card-title">Character Creation</h1>
|
<h1 class="card-title">{{ view.page_title }}</h1>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
{% if form.errors %}
|
{% if form.errors %}
|
||||||
|
|
@ -20,8 +20,8 @@ Manage Characters
|
||||||
<a href="{{ object.get_absolute_url }}"><img class="d-flex mr-3" src="http://placehold.jp/50x50.png" alt="" /></a>
|
<a href="{{ object.get_absolute_url }}"><img class="d-flex mr-3" src="http://placehold.jp/50x50.png" alt="" /></a>
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<p class="float-right ml-2">{{ object.db_date_created }}
|
<p class="float-right ml-2">{{ object.db_date_created }}
|
||||||
<br /><a href="{{ object.get_delete_url }}">Delete</a>
|
<br /><a href="{% url 'character-delete' pk=object.id slug=object.name %}">Delete</a>
|
||||||
<br /><a href="{{ object.get_update_url }}">Edit</a></p>
|
<br /><a href="{% url 'character-update' pk=object.id slug=object.name %}">Edit</a></p>
|
||||||
<h5 class="mt-0"><a href="{{ object.get_absolute_url }}">{{ object }}</a> {% if object.subtitle %}<small class="text-muted" style="white-space:nowrap;">{{ object.subtitle }}</small>{% endif %}</h5>
|
<h5 class="mt-0"><a href="{{ object.get_absolute_url }}">{{ object }}</a> {% if object.subtitle %}<small class="text-muted" style="white-space:nowrap;">{{ object.subtitle }}</small>{% endif %}</h5>
|
||||||
<p>{{ object.db.desc }}</p>
|
<p>{{ object.db.desc }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block titleblock %}
|
||||||
|
Confirm Delete
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
{% load addclass %}
|
||||||
|
<div class="container main-content mt-4" id="main-copy">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-5 offset-lg-3 col-sm-12">
|
||||||
|
<div class="card mt-3 border border-danger">
|
||||||
|
<div class="card-body">
|
||||||
|
<h1 class="card-title">Confirm Delete</h1>
|
||||||
|
<hr />
|
||||||
|
<form method="post" action="?">
|
||||||
|
{% csrf_token %}
|
||||||
|
<p>Are you sure you want to delete "{{ object }}"?</p>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control btn btn-outline-danger" type="submit" value="Submit" />
|
||||||
|
<input type="hidden" name="next" value="{{ next }}" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -7,20 +7,23 @@ templates on the fly.
|
||||||
"""
|
"""
|
||||||
from django.contrib.admin.sites import site
|
from django.contrib.admin.sites import site
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
|
from django.http import HttpResponseRedirect, Http404
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic import View, TemplateView, ListView, DetailView, FormView
|
from django.views.generic import View, TemplateView, ListView, DetailView, FormView
|
||||||
from django.views.generic.edit import DeleteView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||||
|
|
||||||
from evennia import SESSION_HANDLER
|
from evennia import SESSION_HANDLER
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
from evennia.utils import logger
|
from evennia.utils import class_from_module, logger
|
||||||
from evennia.web.website.forms import AccountForm, CharacterForm
|
from evennia.web.website.forms import *
|
||||||
|
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
@ -142,25 +145,132 @@ def admin_wrapper(request):
|
||||||
"""
|
"""
|
||||||
return staff_member_required(site.index)(request)
|
return staff_member_required(site.index)(request)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Class-based views
|
||||||
|
#
|
||||||
|
|
||||||
|
class EvenniaCreateView(CreateView):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def page_title(self):
|
||||||
|
return 'Create %s' % self.model._meta.verbose_name.title()
|
||||||
|
|
||||||
|
class EvenniaUpdateView(UpdateView):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def page_title(self):
|
||||||
|
return 'Update %s' % self.model._meta.verbose_name.title()
|
||||||
|
|
||||||
|
class EvenniaDeleteView(DeleteView):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def page_title(self):
|
||||||
|
return 'Delete %s' % self.model._meta.verbose_name.title()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Object views
|
||||||
|
#
|
||||||
|
|
||||||
class ObjectDetailView(DetailView):
|
class ObjectDetailView(DetailView):
|
||||||
|
|
||||||
model = ObjectDB
|
model = ObjectDB
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
|
"""
|
||||||
|
Override of Django hook.
|
||||||
|
|
||||||
|
Evennia does not natively store slugs, so where a slug is provided,
|
||||||
|
calculate the same for the object and make sure it matches.
|
||||||
|
|
||||||
|
"""
|
||||||
obj = super(ObjectDetailView, self).get_object(queryset)
|
obj = super(ObjectDetailView, self).get_object(queryset)
|
||||||
if not slugify(obj.name) == self.kwargs.get('slug'):
|
if slugify(obj.name) != self.kwargs.get(self.slug_url_kwarg):
|
||||||
raise Http404(u"No %(verbose_name)s found matching the query" %
|
raise Http404(u"No %(verbose_name)s found matching the query" %
|
||||||
{'verbose_name': queryset.model._meta.verbose_name})
|
{'verbose_name': queryset.model._meta.verbose_name})
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
class ObjectCreateView(LoginRequiredMixin, EvenniaCreateView):
|
||||||
|
|
||||||
|
model = ObjectDB
|
||||||
|
|
||||||
|
class ObjectDeleteView(LoginRequiredMixin, ObjectDetailView, EvenniaDeleteView):
|
||||||
|
|
||||||
|
model = ObjectDB
|
||||||
|
template_name = 'website/object_confirm_delete.html'
|
||||||
|
|
||||||
|
def delete(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Calls the delete() method on the fetched object and then
|
||||||
|
redirects to the success URL.
|
||||||
|
|
||||||
|
We extend this so we can capture the name for the sake of confirmation.
|
||||||
|
"""
|
||||||
|
obj = str(self.get_object())
|
||||||
|
response = super(ObjectDeleteView, self).delete(request, *args, **kwargs)
|
||||||
|
messages.success(request, "Successfully deleted '%s'." % obj)
|
||||||
|
return response
|
||||||
|
|
||||||
|
class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
|
||||||
|
|
||||||
|
model = ObjectDB
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
"""
|
||||||
|
Override of Django hook.
|
||||||
|
|
||||||
|
Prepopulates form field values based on object db attributes as well as
|
||||||
|
model field values.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get the object we want to update
|
||||||
|
obj = self.get_object()
|
||||||
|
|
||||||
|
# Get attributes
|
||||||
|
data = {k:getattr(obj.db, k, '') for k in self.form_class.base_fields}
|
||||||
|
|
||||||
|
# Get model fields
|
||||||
|
data.update({k:getattr(obj, k, '') for k in self.form_class.Meta.fields})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
"""
|
||||||
|
Override of Django hook.
|
||||||
|
|
||||||
|
Updates object attributes based on values submitted.
|
||||||
|
|
||||||
|
This method is only called if all values for the fields submitted
|
||||||
|
passed form validation, so at this point we can assume the data is
|
||||||
|
validated and sanitized.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Get the values submitted after they've been cleaned and validated
|
||||||
|
data = {k:v for k,v in form.cleaned_data.items() if k not in self.form_class.Meta.fields}
|
||||||
|
|
||||||
|
# Update the object attributes
|
||||||
|
for key, value in data.items():
|
||||||
|
setattr(self.object.db, key, value)
|
||||||
|
messages.success(self.request, "Successfully updated '%s' for %s." % (key, self.object))
|
||||||
|
|
||||||
|
# Do not return super().form_valid; we don't want to update the model
|
||||||
|
# instance, just its attributes.
|
||||||
|
return HttpResponseRedirect(self.success_url)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Account views
|
||||||
|
#
|
||||||
|
|
||||||
class AccountCreationView(FormView):
|
class AccountMixin(object):
|
||||||
|
|
||||||
|
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||||
form_class = AccountForm
|
form_class = AccountForm
|
||||||
|
|
||||||
|
class AccountCreateView(AccountMixin, ObjectCreateView):
|
||||||
|
|
||||||
template_name = 'website/registration/register.html'
|
template_name = 'website/registration/register.html'
|
||||||
success_url = reverse_lazy('login')
|
success_url = reverse_lazy('login')
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
# Check to make sure basics validated
|
|
||||||
valid = super(AccountCreationView, self).form_valid(form)
|
|
||||||
if not valid: return self.form_invalid(form)
|
|
||||||
|
|
||||||
username = form.cleaned_data['username']
|
username = form.cleaned_data['username']
|
||||||
password = form.cleaned_data['password1']
|
password = form.cleaned_data['password1']
|
||||||
|
|
@ -189,39 +299,39 @@ class AccountCreationView(FormView):
|
||||||
messages.success(self.request, "Your account '%s' was successfully created! You may log in using it now." % account.name)
|
messages.success(self.request, "Your account '%s' was successfully created! You may log in using it now." % account.name)
|
||||||
return HttpResponseRedirect(self.success_url)
|
return HttpResponseRedirect(self.success_url)
|
||||||
|
|
||||||
class CharacterManageView(LoginRequiredMixin, ListView):
|
#
|
||||||
model = ObjectDB
|
# Character views
|
||||||
paginate_by = 10
|
#
|
||||||
template_name = 'website/character_manage_list.html'
|
|
||||||
|
class CharacterMixin(object):
|
||||||
|
|
||||||
|
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
|
||||||
|
form_class = CharacterForm
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
# Get IDs of characters owned by account
|
# Get IDs of characters owned by account
|
||||||
ids = [getattr(x, 'id') for x in self.request.user.db._playable_characters]
|
ids = [getattr(x, 'id') for x in self.request.user.db._playable_characters if x]
|
||||||
|
|
||||||
# Return a queryset consisting of those characters
|
# Return a queryset consisting of those characters
|
||||||
return self.model.objects.filter(id__in=ids).order_by(Lower('db_key'))
|
return self.model.objects.filter(id__in=ids).order_by(Lower('db_key'))
|
||||||
|
|
||||||
class CharacterUpdateView(LoginRequiredMixin, FormView):
|
class CharacterManageView(LoginRequiredMixin, CharacterMixin, ListView):
|
||||||
form_class = CharacterForm
|
|
||||||
template_name = 'website/generic_form.html'
|
paginate_by = 10
|
||||||
success_url = reverse_lazy('manage-characters')
|
template_name = 'website/character_manage_list.html'
|
||||||
fields = ('description',)
|
page_title = 'Manage: Characters'
|
||||||
|
|
||||||
class CharacterDeleteView(LoginRequiredMixin, ObjectDetailView, DeleteView):
|
|
||||||
model = ObjectDB
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
# Restrict characters available for deletion to those owned by
|
|
||||||
# the authenticated account
|
|
||||||
ids = [getattr(x, 'id') for x in self.request.user.db._playable_characters]
|
|
||||||
|
|
||||||
# Return a queryset consisting of those characters
|
class CharacterUpdateView(CharacterMixin, ObjectUpdateView):
|
||||||
return self.model.objects.filter(id__in=ids).order_by(Lower('db_key'))
|
|
||||||
|
form_class = CharacterUpdateForm
|
||||||
|
template_name = 'website/character_form.html'
|
||||||
|
|
||||||
|
class CharacterDeleteView(CharacterMixin, ObjectDeleteView):
|
||||||
|
pass
|
||||||
|
|
||||||
class CharacterCreateView(LoginRequiredMixin, FormView):
|
class CharacterCreateView(CharacterMixin, ObjectCreateView):
|
||||||
form_class = CharacterForm
|
|
||||||
template_name = 'website/character_create_form.html'
|
template_name = 'website/character_form.html'
|
||||||
success_url = reverse_lazy('manage-characters')
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
# Get account ref
|
# Get account ref
|
||||||
|
|
@ -230,12 +340,12 @@ class CharacterCreateView(LoginRequiredMixin, FormView):
|
||||||
|
|
||||||
# Get attributes from the form
|
# Get attributes from the form
|
||||||
self.attributes = {k: form.cleaned_data[k] for k in form.cleaned_data.keys()}
|
self.attributes = {k: form.cleaned_data[k] for k in form.cleaned_data.keys()}
|
||||||
charname = self.attributes.pop('name')
|
charname = self.attributes.pop('db_key')
|
||||||
description = self.attributes.pop('description')
|
description = self.attributes.pop('desc')
|
||||||
|
|
||||||
# Create a character
|
# Create a character
|
||||||
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
|
permissions = settings.PERMISSION_ACCOUNT_DEFAULT
|
||||||
typeclass = settings.BASE_CHARACTER_TYPECLASS
|
typeclass = self.model
|
||||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
||||||
|
|
||||||
from evennia.utils import create
|
from evennia.utils import create
|
||||||
|
|
@ -258,7 +368,7 @@ class CharacterCreateView(LoginRequiredMixin, FormView):
|
||||||
account.db._last_puppet = character
|
account.db._last_puppet = character
|
||||||
|
|
||||||
# Assign attributes from form
|
# Assign attributes from form
|
||||||
[setattr(character.db, field, self.attributes[field]) for field in self.attributes.keys()]
|
[setattr(character.db, key, value) for key,value in self.attributes.items()]
|
||||||
character.db.creator_id = account.id
|
character.db.creator_id = account.id
|
||||||
character.save()
|
character.save()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue