--I implemented in Django to create multiple "projects" like those found in task management tools and manage items within each project. ――The users who can access each project also change.
――It is a tool to assist the theater training plan. ――The "project unit" mentioned in the title is "performance" here. ――For each performance, manage the training schedule, actors, characters, scenes, etc. ---> GitHub repository (Python 3.8.1, Django 3.0.3)
――I couldn't find a sample of what I wanted to do on the net, so I thought about it myself and implemented it. Share it ――If you know a better way, I would be grateful if you could tell me.
First, create "performances" and "performance users", and list the performances that the logged-in user is involved in.
The only field is name
.
production/models.py
from django.db import models
class Production(models.Model):
name = models.CharField('Performance name', max_length=50)
def __str__(self):
return self.name
It is a user for each performance.
I've set up fields for permissions by referring to performances and users (users managed by Django) in ForeignKey
.
See here for ʻAUTH_USER_MODEL`.
In this example, when a performance or user is deleted, the performance user is also deleted.
production/models.py
from django.conf import settings
class ProdUser(models.Model):
'''Users and permissions for each performance
'''
production = models.ForeignKey(Production, verbose_name='Performance',
on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
verbose_name='User', on_delete=models.CASCADE)
is_owner = models.BooleanField('Ownership', default=False)
is_editor = models.BooleanField('Editing rights', default=False)
This is a list of performances that the logged-in user is involved in.
ʻGet the "performance user" such that the userfield is the logged-in user. You may also get it by back-referencing from
self.request.user`.
Since the performance name and "performance ID" can be used indirectly, we will display the list of performances. In other words, if you have the performance user data, you do not need to get the performance itself.
production/views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import ProdUser
class ProdList(LoginRequiredMixin, ListView):
'''Production list view
The model is ProdUser because only the performances of the logged-in user are displayed.
'''
model = ProdUser
template_name = 'production/production_list.html'
def get_queryset(self):
'''Filter the records displayed in the list
'''
#Get your own ProdUser
prod_users = ProdUser.objects.filter(user=self.request.user)
return prod_users
If you write an appropriate URL pattern and template, add "Performance" and "Performance User" on the Admin screen, and log in, a list of performances for which that person is the user will be displayed.
The task management tool is like creating a new project.
If you look at the "Performance List" template above, you will see a link called "New". I press this to bring up the following view. No parameters required.
production/views.py
from django.views.generic.edit import CreateView
from .models import Production
class ProdCreate(LoginRequiredMixin, CreateView):
'''Additional view of Production
'''
model = Production
fields = ('name',) #The only edit item is name
template_name_suffix = '_create'
success_url = reverse_lazy('production:prod_list') #After adding, go to the list
def form_valid(self, form):
'''When passed validation
'''
#Get the saved record
new_prod = form.save(commit=True)
#Add yourself as the owner to the performance user
prod_user = ProdUser(production=new_prod, user=self.request.user,
is_owner=True)
prod_user.save()
messages.success(self.request, str(new_prod) + "is created.")
return super().form_valid(form)
def form_invalid(self, form):
'''When the addition fails
'''
messages.warning(self.request, "Could not create.")
return super().form_invalid(form)
The page is simple because the field is just name
(performance name), but the logged-in user becomes the" performance user "and is also given ownership.
Now the logged-in user can create their own "performance".
Click each performance in the performance list to move to the top view of that performance. From here, each performance will be a different world.
Looking at the performance list template above, the performance name part is the link that opens the top view of that "performance".
production/templates/production/production_list.html
<tr>
<td>
<a href="{% url 'rehearsal:rhsl_top' prod_id=item.production.id %}">
{{ item.production }}</a>
</td>
<td>{{ item.is_owner }}</td>
<td>{{ item.is_editor }}</td>
</tr>
I created the top view in an application called rehearsal
, so it looks like this.
This link requires a "performance ID" as a parameter (the parameter name in the code is prod_id
).
Since ʻitem being displayed is a "performance user", pass ʻitem.production.id
to the "performance ID".
The view is a simple menu screen.
rehearsal/views/views.py
from django.views.generic import TemplateView
from django.core.exceptions import PermissionDenied
from .view_func import *
class RhslTop(LoginRequiredMixin, TemplateView):
'''Rehearsal top page
'''
template_name = 'rehearsal/top.html'
def get(self, request, *args, **kwargs):
'''Handler that received the request at the time of display
'''
#Obtain performance users from access information and inspect access rights
prod_user = accessing_prod_user(self)
if not prod_user:
raise PermissionDenied
#Have production as an attribute of view
self.production = prod_user.production
return super().get(request, *args, **kwargs)
The reason we have the production
("performance" object) as an attribute of view is to reference it from the template.
ʻAccessing_prod_user ()` is a function defined in another module called view_func.py and you are logged in. If the user of is the user of the performance, then the "performance user" object is returned.
rehearsal/views/view_func.py
from production.models import ProdUser
def accessing_prod_user(view, prod_id=None):
'''Get the corresponding ProdUser from the access information
Parameters
----------
view : View
View from which access information is obtained
prod_id : int
Prod in URLconf_Specify if there is no id
'''
if not prod_id:
prod_id=view.kwargs['prod_id']
prod_users = ProdUser.objects.filter(
production__pk=prod_id, user=view.request.user)
if len(prod_users) < 1:
return None
return prod_users[0]
If the logged-in user is not a performance user, it returns None
, which allows access control.
It's just a linked list, but every link has a prod_id
parameter.
Now that we have the groundwork to divide the data in units called "performances", we will be able to handle the data belonging to each performance.
It is the data of the actors involved in the performance.
The data belonging to the performance should have a ForeignKey
called production
to decide which performance it belongs to.
rehearsal/models.py
class Actor(models.Model):
'''actor
'''
production = models.ForeignKey(Production, verbose_name='Performance',
on_delete=models.CASCADE)
name = models.CharField('name', max_length=50)
short_name = models.CharField('Abbreviated name', max_length=5, blank=True)
class Meta:
verbose_name = verbose_name_plural = 'actor'
def __str__(self):
return self.name
def get_short_name(self):
return self.short_name or self.name[:3]
short_name
is used when displaying in tabular format.
It is a view that transitions from the top view of the performance by clicking "Actor List". Of course, only the actors of the performance are displayed, and it cannot be seen by anyone other than the "performance user" of the performance.
Before looking at the actor list, let's take a look at the link to the actor list in the top view of the performance.
rehearsal/templates/rehearsal/top.html
<li><a href="{% url 'rehearsal:actr_list' prod_id=view.production.id %}">
List of actors</a></li>
Since we had a production
("performance" object) as an attribute of view, we can get the "performance ID" in the form view.production.id
.
By passing it to the prod_id
parameter, you will be taken to the list of actors for that performance.
Now, let's take a look at the list of actors.
rehearsal/views/views.py
class ActrList(ProdBaseListView):
'''List view of Actor
'''
model = Actor
def get_queryset(self):
'''Filter the records displayed in the list
'''
prod_id=self.kwargs['prod_id']
return Actor.objects.filter(production__pk=prod_id)\
.order_by('name')
The inherited ProdBaseListView
is an abstract class for displaying a list of data belonging to a performance.
Since we will create other views like this, we have put together an abstract class for access control and so on.
Let's see this too.
rehearsal/views/views.py
class ProdBaseListView(LoginRequiredMixin, ListView):
'''ListView Base class to check permissions
'''
def get(self, request, *args, **kwargs):
'''Handler to receive the request at the time of display
'''
#Obtain performance users from access information and inspect access rights
prod_user = accessing_prod_user(self)
if not prod_user:
raise PermissionDenied
#Have the accessing ProdUser as an attribute of view
#To decide if there is an add button in the template
self.prod_user = prod_user
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
'''Modify the parameters passed to the template
'''
context = super().get_context_data(**kwargs)
#back button,Prod for add button_set id
context['prod_id'] = self.kwargs['prod_id']
return context
The performance ID (prod_id
) is included in the context
so that it can be easily referenced from the template.
You can refer to it by doing view.prod_user.production.id
without putting it in context
, but it is long.
Let's add some "actors" on the Admin screen to display the list of actors.
If you look at the "Actor List" template above, you'll see a link called "Add". And it is only visible to users who own or edit the performance. Of course, users who can't see the link can open the URL directly in the browser, so access control is also required for the view to which the actor is added.
Again, we are creating an abstract class similar to the ProdBaseListView
above.
Let's look at the abstract class first.
rehearsal/views/views.py
class ProdBaseCreateView(LoginRequiredMixin, CreateView):
'''CreateView Base class to check permissions
'''
def get(self, request, *args, **kwargs):
'''Handler to receive the request at the time of display
'''
#Inspect edit rights to get access users
prod_user = test_edit_permission(self)
#Have production as an attribute of view
#To display as a fixed element in the template
self.production = prod_user.production
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
'''Handler to receive request at save
'''
#Inspect edit rights to get access users
prod_user = test_edit_permission(self)
#Have production as an attribute of view
#To set in the instance when saving
self.production = prod_user.production
return super().post(request, *args, **kwargs)
def form_valid(self, form):
'''When passed validation
'''
#Set the production of the record you want to add
instance = form.save(commit=False)
instance.production = self.production
messages.success(self.request, str(instance) + "Added.")
return super().form_valid(form)
def form_invalid(self, form):
'''When the addition fails
'''
messages.warning(self.request, "It could not be added.")
return super().form_invalid(form)
test_edit_permission ()
is a function defined in view_func.py by the logged-in user. If you own or edit the performance, it returns the "performance user".
def test_edit_permission(view, prod_id=None):
'''Check editing rights
Returns
-------
prod_user : ProdUser
Accessing ProdUser who has edit rights
'''
#Get performance users from access information
prod_user = accessing_prod_user(view, prod_id=prod_id)
if not prod_user:
raise PermissionDenied
#Deny access if you do not have ownership or editing rights
if not (prod_user.is_owner or prod_user.is_editor):
raise PermissionDenied
return prod_user
The permissions are checked both when displaying the view (get
method) and when pressing the" Add "button for additional processing (post
method).
In addition, this abstract class also implements the common process of "when adding data belonging to a performance, set the performance in the production
field ".
Now for the view to add an actor.
rehearsal/views/views.py
class ActrCreate(ProdBaseCreateView):
'''Additional view of Actor
'''
model = Actor
fields = ('name', 'short_name')
def get_success_url(self):
'''Dynamically give the transition destination when the addition is successful
'''
prod_id = self.production.id
url = reverse_lazy('rehearsal:actr_list', kwargs={'prod_id': prod_id})
return url
After adding it, you will be returned to the list of actors "for that performance".
――It is a tool that assists the rehearsal plan of the play, and I made multiple "performances" and separated the data for each performance.
――We created a model called "Performance User" so that access can be controlled for each user (user managed by Django).
--The data belonging to the performance (actors, etc.) has a ForeignKey
field called production
, and when accessed from the performance menu, it is filtered by that.
--When accessing the view to write data such as additions and changes, the edit right is checked both when viewing and writing.
--When adding new data belonging to a performance, the performance is automatically set in the production
field.
-Django divides data into project units (2)
Recommended Posts