Ceci est la suite de cet article. Une «unité de type projet» est un «projet» dans un outil de gestion de tâches, etc. Ici, puisque le théâtre est géré, la «performance» lui correspond.
---> Dépôt GitHub (Python 3.8.1, Django 3.0.3)
La dernière fois, j'ai pensé à l'interface de saisie de données qui appartient à la "performance". Cette fois, j'aimerais réfléchir à un mécanisme pour inviter des utilisateurs autres que moi et les ajouter aux membres de la "performance".
—— Permet au propriétaire d'une «performance» de créer une «invitation» pour un compte particulier.
Ceux qui invitent
Invité
Dans cette application Web, ["Performance User"](https://qiita.com/satamame/items/959e21ade18c48e1b4d6#%E5%85%AC%E6%BC%94%E3%83%A6%E3%83%BC% E3% 82% B6% E3% 81% AE% E3% 83% A2% E3% 83% 87% E3% 83% AB) aux comptes gérés par Django et à chaque «performance» Le statut de participation est associé. Par conséquent, le processus lorsque la personne invitée sélectionne «Rejoindre» est le processus de création d'un nouvel «utilisateur de performance».
Il contient les champs suivants.
champ | Contenu |
---|---|
production | Invité "performance" |
inviter | Personne invitante(Comptes gérés par Django) |
invitee | Personne invitée(Comptes gérés par Django) |
exp_dt | Date d'expiration de l'invitation |
--Il y a un écran appelé "Liste des membres", alors créez un bouton appelé "Inviter" ici. --Lorsque vous appuyez sur ce bouton, l'écran de création d'une «invitation» s'affiche. ――Sur l'écran pour créer une «invitation», vous devez pouvoir spécifier la «personne invitée» (le contenu des autres champs est décidé par l'application).
--Lorsque le propriétaire de "Performance" affiche l'écran "Liste des membres", la "Carte d'invitation" qu'il a créée sera affichée.
from datetime import datetime, timezone
from django.conf import settings
from django.db import models
class Invitation(models.Model):
'''Invitation à la troupe
'''
production = models.ForeignKey(Production, verbose_name='Performance',
on_delete=models.CASCADE)
inviter = models.ForeignKey(settings.AUTH_USER_MODEL,
verbose_name='Personne invitante',
related_name='inviter', on_delete=models.CASCADE)
invitee = models.ForeignKey(settings.AUTH_USER_MODEL,
verbose_name='Personne invitée',
related_name='invitee', on_delete=models.CASCADE)
exp_dt = models.DateTimeField(verbose_name='Date limite')
class Meta:
verbose_name = verbose_name_plural = 'Invitation à la troupe'
ordering = ['exp_dt']
def __str__(self):
return f'{self.invitee}À{self.production}Invitation à'
def expired(self):
'''Renvoie si cette invitation a expiré
'''
now = datetime.now(timezone.utc)
return now > self.exp_dt
--Production
est défini dans cette application Web (https://qiita.com/satamame/items/959e21ade18c48e1b4d6#%E5%85%AC%E6%BC%94-%E3%83%97%E3% 83% AD% E3% 82% B8% E3% 82% A7% E3% 82% AF% E3% 83% 88% E7% 9A% 84% E5% 8D% 98% E4% BD% 8D-% E3% 81 % AE% E3% 83% A2% E3% 83% 87% E3% 83% AB) Il s'agit d'un modèle de «performance».
―― Puisque ʻinviter et ʻinvitee
sont des comptes gérés par Django, ils font référence à l'objet de settings.AUTH_USER_MODEL
.
Vous trouverez ci-dessous une description des vues et des modèles.
Puisqu'il est créé dans une application appelée «production», l'URL est spécifiée comme «production: usr_list».
Voir Source pour production / urls.py
pour savoir comment écrire ʻurls.py` pour l'appel. S'il vous plaît.
Créez une vue qui hérite de «CreateView» pour créer une invitation.
views.py
from django.views.generic.edit import CreateView
from django.contrib import messages
from django.contrib.auth import get_user_model
from .view_func import *
from .models import Production, ProdUser, Invitation
class InvtCreate(LoginRequiredMixin, CreateView):
'''Vue supplémentaire de l'invitation
'''
model = Invitation
fields = ()
views.py
def get(self, request, *args, **kwargs):
'''Handler pour recevoir la demande au moment de l'affichage
'''
#Inspectez la propriété pour accéder aux utilisateurs performants
prod_user = test_owner_permission(self)
#Avoir la production comme attribut de vue
#Pour afficher comme élément fixe dans le modèle
self.production = prod_user.production
return super().get(request, *args, **kwargs)
test_owner_permission ()
pour autoriser l'accès uniquement aux utilisateurs qui possèdent la performance.production / view_func.py
pour ce que fait cette méthode. ..views.py
def post(self, request, *args, **kwargs):
'''Le gestionnaire recevra la demande lors de l'enregistrement
'''
#Inspectez la propriété pour accéder aux utilisateurs performants
prod_user = test_owner_permission(self)
#"ID de la personne invitée" saisi dans le formulaire
invitee_value = request.POST['invitee_id']
#Avoir un utilisateur correspondant comme attribut de vue
user_model = get_user_model()
invitees = user_model.objects.filter(username=invitee_value)
if len(invitees) > 0:
self.invitee = invitees[0]
# prod_user,Avoir la production comme attribut de vue
#À utiliser lors de la validation et du stockage
self.prod_user = prod_user
self.production = prod_user.production
return super().post(request, *args, **kwargs)
――Sur l'écran de création d'une invitation, dans le cadre de l'interface utilisateur, la "personne invitée" est écrite comme la "personne invitée".
post ()
.
--ʻUserUtilisez
get_user_model ()pour référencer le modèle ([> Documentation](https://docs.djangoproject.com/ja/3.0/topics/auth/customizing/#referencing-the-user) -modèle)). Parce que
settings.AUTH_USER_MODEL` est une chaîne.views.py
def form_valid(self, form):
'''Une fois la validation réussie
'''
#L'ajout a échoué si le gestionnaire POST n'a pas réussi à inviter l'utilisateur
if not hasattr(self, 'invitee'):
return self.form_invalid(form)
#Liste des ID utilisateur des utilisateurs performants
prod_users = ProdUser.objects.filter(production=self.production)
prod_user_user_ids = [prod_user.user.id for prod_user in prod_users]
#Liste d'identifiants des utilisateurs invités
invitations = Invitation.objects.filter(production=self.production)
current_invitee_ids = [invt.invitee.id for invt in invitations]
#Il n'est pas possible d'inviter des utilisateurs performants ou des utilisateurs invités.
if self.invitee.id in prod_user_user_ids\
or self.invitee.id in current_invitee_ids:
return self.form_invalid(form)
#Définissez chaque champ de l'enregistrement que vous souhaitez ajouter
instance = form.save(commit=False)
instance.production = self.production
instance.inviter = self.prod_user.user
instance.invitee = self.invitee
#Le délai est de 7 jours
#Enregistré en UTC par défaut, mais spécifiez UTC juste au cas où
instance.exp_dt = datetime.now(timezone.utc) + timedelta(days=7)
messages.success(self.request, str(instance.invitee) + "Était invité.")
return super().form_valid(form)
fields
est vide (parce que Form
n'a rien à valider), tout utilisateur essayant de créer une invitation passera par cette méthode.views.py
def get_success_url(self):
'''Donner dynamiquement la destination de la transition lorsque l'ajout est réussi
'''
prod_id = self.prod_user.production.id
url = reverse_lazy('production:usr_list', kwargs={'prod_id': prod_id})
return url
def form_invalid(self, form):
'''Lorsque l'ajout échoue
'''
messages.warning(self.request, "Je ne pourrais pas vous inviter.")
return super().form_invalid(form)
--get_success_url ()
etform_invalid ()
en cas d'échec.
production: user_list
) est la vue (écran" Liste des membres ") où vous essayez de placer le bouton" Inviter ".invitation_form.html
{% extends 'base.html' %}
{% block content %}
<h1 style="margin: 0px;">
<a href="{% url 'production:usr_list' prod_id=view.production.id %}">◀</a>
Invitation à la troupe
</h1>
<div> </div>
<form method="post">
{% csrf_token %}
<table>
<tr><th>Performance</th><td>{{ view.production }}</td></tr>
<tr><th>ID de la personne à inviter</th><td><input type="text" name="invitee_id" /></td></tr>
</table>
<input type="submit" value="Invitation">
</form>
{% endblock %}
L'écran qui affiche l'invitation que vous avez créée est l'écran "Liste des membres". Nous allons également mettre en place un bouton "inviter" ici. Le scénario utilisateur est le suivant.
views.py
from django.views.generic import ListView
from django.core.exceptions import PermissionDenied
class UsrList(LoginRequiredMixin, ListView):
'''Vue de la liste des producteurs
'''
model = ProdUser
def get(self, request, *args, **kwargs):
'''Handler pour recevoir la demande au moment de l'affichage
'''
#Obtenez des utilisateurs performants à partir des informations d'accès et inspectez les droits d'accès
prod_user = accessing_prod_user(self, kwargs['prod_id'])
if not prod_user:
raise PermissionDenied
#Faites-en un attribut de vue afin qu'il puisse être référencé à partir du modèle
self.prod_user = prod_user
#Faites-en un attribut de la vue pour afficher les utilisateurs invités
self.invitations = Invitation.objects.filter(production=prod_user.production)
return super().get(request, *args, **kwargs)
#Ce qui suit est omis
――Originalement, c'est une vue qui affiche une liste d '"utilisateurs de performance", mais afin d'afficher une liste d'invitations sur le même écran, les invitations sont définies sur l'attribut Vue (ʻinvitations`) afin qu'elles puissent être référencées à partir du modèle. ――Dans la section précédente, j'ai écrit "Carte d'invitation que j'ai faite", mais à proprement parler, j'ai obtenu "Carte d'invitation pour cette représentation". La raison est qu'il sera affiché même si le propriétaire de la "performance" change.
Seule la partie affichant l'invitation est extraite. Pour tout voir, accédez à Code sur GitHub.
produser_list.html
{% if view.prod_user.is_owner and view.invitations %}
<div style="border:solid thin lightgray; border-radius:10px; padding: 10px; margin:5px 0 20px;">
<p><strong>Inviter un utilisateur</strong></p>
<table>
<tr>
<th>Identifiant d'utilisateur</th>
<th>Nom de famille</th>
<th>Nom</th>
<th>Date limite d'invitation</th>
</tr>
{% for item in view.invitations %}
<tr>
<td>{{ item.invitee.username }}</td>
<td>{{ item.invitee.last_name }}</td>
<td>{{ item.invitee.first_name }}</td>
{% if item.expired %}
<td style="color:red;">{{ item.exp_dt }}</td>
{% else %}
<td>{{ item.exp_dt }}</td>
{% endif %}
<td><a href="{% url 'production:invt_delete' pk=item.id from='usr_list' %}" class="deletelink">Effacer</a></td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
--Dans la première instruction ʻif, affiche la liste des invitations à condition que l'utilisateur accédant soit le propriétaire de la performance et que la vue ait l'attribut ʻinvitations
(et ne soit pas vide). Je le fais.
de la classe ʻInvitation
créée ci-dessus.from
à l'URL afin que je puisse me souvenir de la destination de retour.L'écran qui affiche l'invitation que vous avez reçue est l'écran «Performances participantes». À l'origine, il s'agit d'un écran qui affiche une liste des performances participantes, mais tout comme lors de l'affichage de l'invitation créée, la zone de l'invitation n'est affichée que lorsque les conditions sont remplies.
views.py
class ProdList(LoginRequiredMixin, ListView):
'''Vue de la liste de production
Le modèle est ProdUser car seules les performances de l'utilisateur connecté sont affichées.
'''
model = ProdUser
template_name = 'production/production_list.html'
def get(self, request, *args, **kwargs):
'''Handler pour recevoir la demande au moment de l'affichage
'''
#Faites-en un attribut de vue pour afficher les invitations à la troupe
now = datetime.now(timezone.utc)
self.invitations = Invitation.objects.filter(invitee=self.request.user,
exp_dt__gt=now)
return super().get(request, *args, **kwargs)
#Ce qui suit est omis
La méthode n'est pas utilisée et elle est extraite par
filter ()` de QuerySet.xx__gt = xx
comme vous le faites ici. S'il te plait donne moi.Seule la partie affichant l'invitation est extraite. Pour tout voir, accédez à Code sur GitHub.
production_list.html
{% if view.invitations %}
<div style="border:solid thin lightgray; border-radius:10px; padding: 10px; margin:5px 0 20px;">
<p><strong>Groupe invité</strong></p>
<table>
<tr>
<th>Nom de la performance</th>
<th>ID de l'invitant</th>
<th>Date limite d'invitation</th>
</tr>
{% for item in view.invitations %}
<tr>
<td>{{ item.production }}</td>
<td>{{ item.inviter.username }}</td>
{% if item.expired %}
<td style="color:red;">{{ item.exp_dt }}</td>
{% else %}
<td>{{ item.exp_dt }}</td>
{% endif %}
<td>
<a href="{% url 'production:prod_join' invt_id=item.id %}" class="addlink">Participation</a>
<a href="{% url 'production:invt_delete' pk=item.id from='prod_list' %}" class="deletelink">Effacer</a>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
--En plus du bouton "Supprimer", le bouton "Rejoindre" sera affiché sur l'invitation reçue. --Lorsque vous appuyez sur le bouton «Rejoindre», vous serez redirigé vers la vue qui confirme et traite votre participation.
Lors de l'affichage des invitations que j'ai faites et des invitations que j'ai reçues, j'ai placé un bouton «Supprimer» dans le modèle.
Créez une vue à laquelle vous pouvez accéder à partir de ce lien (production: invoke_delete
).
views.py
from django.views.generic.edit import DeleteView
class InvtDelete(LoginRequiredMixin, DeleteView):
'''Vue de suppression d'invitation
'''
model = Invitation
template_name_suffix = '_delete'
-- template_name_suffix
semble être "_confirm_delete "par défaut.
Cette zone est votre choix.
views.py
def get(self, request, *args, **kwargs):
'''Handler pour recevoir la demande au moment de l'affichage
'''
#Vérifiez que vous êtes le propriétaire du spectacle ou l'invité de l'invitation
invt = self.get_object()
prod_id = invt.production.id
prod_user = accessing_prod_user(self, prod_id)
is_owner = prod_user and prod_user.is_owner
is_invitee = self.request.user == invt.invitee
if not (is_owner or is_invitee):
raise PermissionDenied
return super().get(request, *args, **kwargs)
Pour ce que fait la méthode, voir [Source pour
production / view_func.py`](https://github.com/satamame/pscweb2/blob/master/production/view_func.py) S'il te plait regarde.views.py
def post(self, request, *args, **kwargs):
'''Le gestionnaire recevra la demande lors de l'enregistrement
'''
#Vérifiez que vous êtes le propriétaire du spectacle ou l'invité de l'invitation
invt = self.get_object()
prod_id = invt.production.id
prod_user = accessing_prod_user(self, prod_id)
is_owner = prod_user and prod_user.is_owner
is_invitee = self.request.user == invt.invitee
if not (is_owner or is_invitee):
raise PermissionDenied
return super().post(request, *args, **kwargs)
POST
fait est la même chose que GET
.
Si vous ne vous sentez pas à l'aise avec la duplication, vous pouvez en faire une méthode.views.py
def get_success_url(self):
'''Donner dynamiquement la destination de la transition lorsque la suppression est réussie
'''
if self.kwargs['from'] == 'usr_list':
prod_id = self.object.production.id
url = reverse_lazy('production:usr_list', kwargs={'prod_id': prod_id})
else:
url = reverse_lazy('production:prod_list')
return url
def delete(self, request, *args, **kwargs):
'''Message une fois supprimé
'''
result = super().delete(request, *args, **kwargs)
messages.success(
self.request, str(self.object) + "A été supprimé.")
return result
Puisque le paramètre from
a été ajouté à l'URL du lien du bouton" Supprimer ", la destination de retour est triée à l'aide de ce paramètre.
Vous pouvez accéder à ʻURLConf avec
self.kwargsmême si vous n'êtes pas dans les méthodes
get ()ou
post (). --ʻURLConf
est spécifié dans production / urls.py
comme suit (> Source ).
path('invt_delete/<int:pk>/<str:from>/', views.InvtDelete.as_view(), name='invt_delete'),
C'est une page simple avec seulement un bouton «Supprimer» et un bouton «Annuler».
invitation_delete.html
{% extends 'base.html' %}
{% block content %}
<h1 style="margin: 0px;">
{% if view.kwargs.from == 'usr_list' %}
<a href="{% url 'production:usr_list' prod_id=object.production.id %}">◀</a>
{% else %}
<a href="{% url 'production:prod_list' %}">◀</a>
{% endif %}
Supprimer l'invitation à la troupe
</h1>
<form method="post">{% csrf_token %}
<p>Voulez-vous supprimer cette invitation?</p>
<input type="submit" value="effacer">
<input type="button"
{% if view.kwargs.from == 'usr_list' %}
onclick="location.href='{% url 'production:usr_list' prod_id=object.production.id %}'"
{% else %}
onclick="location.href='{% url 'production:prod_list' %}'"
{% endif %}
value="Annuler">
</form>
<table>
<tr><th>Performance</th><td>{{ object.production }}</td></tr>
<tr><th>ID de la personne invitée</th><td>{{ object.invitee.username }}</td></tr>
<tr><th>Nom de famille</th><td>{{ object.invitee.last_name }}</td></tr>
<tr><th>Nom</th><td>{{ object.invitee.first_name }}</td></tr>
<tr><th>Date limite</th>
{% if object.expired %}
<td style="color:red;">{{ object.exp_dt }}</td>
{% else %}
<td>{{ object.exp_dt }}</td>
{% endif %}
</tr>
</table>
{% endblock %}
from
est" liste_produit "(si la personne invitée l'ouvre).dans un modèle, utilisez quelque chose comme
view.kwargs.from` (si" from "est le nom du paramètre).Le processus lorsque "Rejoindre" est sélectionné est le processus de création d'un nouvel "Utilisateur de performance", donc créez le CreateView
suivant.
Cependant, le nom de Template est clairement nommé "production_join.html".
views.py
from django.http import Http404
class ProdJoin(LoginRequiredMixin, CreateView):
'''Voir pour participer à la performance
'''
model = ProdUser
fields = ('production', 'user')
template_name = 'production/production_join.html'
success_url = reverse_lazy('production:prod_list')
views.py
def get(self, request, *args, **kwargs):
'''Handler pour recevoir la demande au moment de l'affichage
'''
#Vérifiez si vous êtes invité et obtenez une performance à laquelle vous pouvez participer
self.production = self.production_to_join()
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
'''Le gestionnaire recevra la demande lors de l'enregistrement
'''
#Vérifiez si vous êtes invité et obtenez une performance à laquelle vous pouvez participer
self.production = self.production_to_join()
return super().post(request, *args, **kwargs)
--Dans les gestionnaires GET
et POST
, en fonction des informations de ʻURLConf`, définissez les performances auxquelles participer en tant qu'attribut.
production_to_join ()
pour obtenir l'objet de la performance (Production
) de la destination de participation est la suivante.views.py
def production_to_join(self):
'''Vérifiez si vous êtes invité et renvoyez un spectacle auquel vous pouvez assister
'''
#Lancer une erreur 404 si vous n'êtes pas invité
invt_id = self.kwargs['invt_id'];
invts = Invitation.objects.filter(id=invt_id)
if len(invts) < 1:
raise Http404
invt = invts[0]
#Lancer une erreur 404 si l'invitation a expiré
now = datetime.now(timezone.utc)
if now > invt.exp_dt:
raise Http404
#PermissionDenied si l'utilisateur accédant n'est pas invité
user = self.request.user
if user != invt.invitee:
raise PermissionDenied
return invt.production
-Au ["Mécanisme d'affichage des invitations reçues"](#Mécanisme d'affichage des invitations reçues), le paramètre ʻinvt_id est intégré dans le lien du bouton "Rejoindre", donc invitez à utiliser ceci. Nous obtenons un objet d'état (ʻInvitation
).
Le chemin pour arriver ici (vue pour participer à la performance) est le suivant sur ʻurls.py` (> Source).
path('prod_join/<int:invt_id>/', views.ProdJoin.as_view(), name='prod_join'),
Il est préférable d'afficher un message sur la date d'expiration, mais ici, il renvoie simplement 404.
views.py
def form_valid(self, form):
'''Une fois la validation réussie
'''
#Inspecter l'invitation
invt_id = self.kwargs['invt_id'];
invts = Invitation.objects.filter(id=invt_id)
if len(invts) < 1:
return self.form_invalid(form)
invt = invts[0]
#Obtenez l'enregistrement à sauvegarder
new_prod_user = form.save(commit=False)
#Les performances correctes sont-elles définies?
if new_prod_user.production != invt.production:
return self.form_invalid(form)
#L'utilisateur correct est-il défini?
if new_prod_user.user != invt.invitee:
return self.form_invalid(form)
#Supprimer l'invitation
invt.delete()
messages.success(self.request, str(invt.production) + "J'ai participé à.")
return super().form_valid(form)
def form_invalid(self, form):
'''Lorsque vous ne parvenez pas à participer
'''
messages.warning(self.request, "Je n'ai pas pu participer.")
return super().form_invalid(form)
Form
( production
et ʻuser dans ce cas), la méthode de View
form_valid () `est appelée, alors faites-la correspondre à nouveau avec le contenu de l '" invitation ".C'est une page simple avec seulement un bouton «Rejoindre» et un bouton «Plus tard».
production_join.html
{% extends 'base.html' %}
{% block content %}
<h1 style="margin: 0px;">
<a href="{% url 'production:prod_list' %}">◀</a>
Participez à la performance
</h1>
<div> </div>
<p>
{{ view.production }}Ont été invités à. Etes-vous sûr de vouloir participer?
</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="production" value="{{ view.production.id }}">
<input type="hidden" name="user" value="{{ view.request.user.id }}">
<input type="submit" value="Participation">
<input type="button"
onclick="location.href='{% url 'production:prod_list' %}'"
value="plus tard">
</form>
{% endblock %}
POST
soit caché et écrit directement.
--Si vous voulez écrire directement une valeur de champ de type ForeignKey
, utilisez l''id` de l'objet.Nous avons introduit l'implémentation de la fonction d'inviter les utilisateurs à une «unité de projet» (groupe) appelée «performance» et de permettre aux utilisateurs invités d'y participer. Les techniques utilisées sont résumées ci-dessous.
settings.AUTH_USER_MODEL
pour spécifier le type ʻUser comme champ de modèle, et
get_user_model () pour faire référence au modèle ʻUser
dans la méthode View.get_user_model ()
puisse être utilisé même en le spécifiant comme champ de modèle.) dans le code de la vue qui crée l'objet, vous pouvez utiliser
CreateView en vidant
les champs. --Si vous ne voulez pas que Template utilise une interface utilisateur sélective, vous pouvez maintenant entrer la valeur d'un champ (tel que ʻusername
) pour trouver l'objet et le trouver dans le code (méthode post ()
). ..hasattr ()
). J'ai pu valider.
--Si vous définissez les données que vous souhaitez afficher sur l'attribut View, vous pouvez vérifier l'existence de l'attribut (qu'il soit vide ou non) du côté Tamplate et l'afficher si nécessaire.de
à l'URL de cette vue.kwargs
reçus par les méthodes get () ʻet
post () pourraient plus tard être référencés comme
self.kwargs`.view.kwargs.Parameter name
dans Template.GET
, vous pouvez également l'écrire comme caché dans Template.
--Si vous voulez écrire une valeur de champ de type ForeignKey
directement dans le Template (comme caché etc.), vous pouvez utiliser l''id de l'objet et il sera géré correctement par
Form`.Recommended Posts