Dites que je veux créer une vue basée sur les classes dont mises à jour et crée un objet. D'une question précédente J'ai travaillé sur je pourrais faire l'une des choses suivantes:
1) Utilisez 2 vues génériques CreateView
et UpdateView
, ce qui signifierait, selon moi, que deux URL pointent vers deux classes différentes.
2) Utilisez une vue basée sur les classes qui hérite de la base View
, ce qui signifierait, selon moi, que deux URL pointent vers une seule classe (que j'ai créée et qui hérite de View
).
J'ai deux questions:
a) Quel est le meilleur?
b) ccbv.co.uk montre une View
de base, mais je ne vois aucune méthode get, post etc. décrite, est-ce correct?
Pourquoi devez-vous gérer la création et la mise à jour à l'aide d'une seule vue? Il est beaucoup plus simple de disposer de deux vues distinctes, chacune héritant de sa classe de vues générique respective. Ils peuvent partager le même formulaire et le même modèle si vous le souhaitez. Ils sont probablement servis à partir d'URL différentes. Je ne vois donc pas ce que vous obtiendriez en le faisant en une seule vue.
Donc: utilisez deux vues, l’une héritant de CreateView
et l’autre de UpdateView
. Celles-ci gèrent quasiment tout ce dont vous pourriez avoir besoin, tandis que la seconde approche nécessiterait de réinventer la roue vous-même. Si vous avez un code "Entretien" commun utilisé à la fois lors de la création ou de la mise à jour d'objets, utilisez un mixin ou créez votre propre vue couvrant les deux cas d'utilisation, en héritant de CreateView
et UpdateView
.
Je me suis retrouvé dans une situation où je voulais quelque chose comme ça. Voici ce que j'ai proposé (notez que si vous essayez de l'utiliser comme une vue de mise à jour et que l'objet demandé ne peut pas être trouvé, il se comportera comme une vue de création plutôt que de lancer un 404):
from Django.views.generic.detail import SingleObjectTemplateResponseMixin
from Django.views.generic.edit import ModelFormMixin, ProcessFormView
class CreateUpdateView(SingleObjectTemplateResponseMixin, ModelFormMixin,
ProcessFormView):
def get_object(self, queryset=None):
try:
return super(CreateUpdateView,self).get_object(queryset)
except AttributeError:
return None
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(CreateUpdateView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(CreateUpdateView, self).post(request, *args, **kwargs)
Il s'avère que UpdateView
et CreateView
héritent de exactement les mêmes classes et mixins. La seule différence réside dans les méthodes get/post. Voici comment ils sont définis dans le source Django (1.8.2):
class BaseCreateView(ModelFormMixin, ProcessFormView):
"""
Base view for creating an new object instance.
Using this base class requires subclassing to provide a response mixin.
"""
def get(self, request, *args, **kwargs):
self.object = None
return super(BaseCreateView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = None
return super(BaseCreateView, self).post(request, *args, **kwargs)
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
"""
View for creating a new object instance,
with a response rendered by template.
"""
template_name_suffix = '_form'
class BaseUpdateView(ModelFormMixin, ProcessFormView):
"""
Base view for updating an existing object.
Using this base class requires subclassing to provide a response mixin.
"""
def get(self, request, *args, **kwargs):
self.object = self.get_object()
return super(BaseUpdateView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
return super(BaseUpdateView, self).post(request, *args, **kwargs)
class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
"""
View for updating an object,
with a response rendered by template.
"""
template_name_suffix = '_form'
Comme vous pouvez le constater, les méthodes getView et post de CreateView définissent self.object = None
alors que UpdateView
le définit sur self.get_object()
. Tout ce que j'ai fait est de combiner ces deux méthodes dans ma méthode CreateUpdateView.get_object
qui tente d'appeler la get_object
de la classe parente et renvoie None
au lieu de déclencher une exception s'il n'y a pas d'objet.
Pour servir une page 404 lorsqu'elle est utilisée comme vue de mise à jour, vous pouvez probablement remplacer as_view
et lui transmettre un argument booléen update_only
. Si update_only
est True
et que la vue ne trouve pas l'objet, déclenchez le 404.
Comme suggéré par @scubabuddha J'ai rencontré une situation similaire et j'ai utilisé sa réponse modifiée en @ mario-orlandi suggéré dans son commentaire:
from Django.views.generic import UpdateView
class CreateUpdateView(UpdateView):
def get_object(self, queryset=None):
try:
return super().get_object(queryset)
except AttributeError:
return None
J'ai utilisé cette solution avec Django 1.11 mais je pense que cela peut fonctionner avec Django 2.0.
Je confirme que cette solution fonctionne avec Django 2.0 et 2.1.
Pour partager du code entre vos UpdateView
et CreateView
, au lieu de créer une classe combinée, vous pouvez utiliser une super-classe commune en tant que mixin. De cette façon, il serait peut-être plus facile de séparer les différentes préoccupations. Et vous pouvez réutiliser beaucoup de code Django existant.
class BookFormView(PJAXContextMixin):
template_name = 'library/book_form.html'
form_class = BookForm
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
class Meta:
abstract = True
class BookCreateView(BookFormView, CreateView):
pass
class FormatUpdateView(BookFormView, UpdateView):
queryset = Book.objects
Vous pouvez également utiliser Django Smartmin qui s’inspire du CBV de Django. Voici un exemple tiré de la documentation: https://smartmin.readthedocs.org/en/latest/quickstart.html
La solution la plus simple et la meilleure de toutes link
class WorkerUpdate(UpdateView):
form_class = WorkerForm
def get_object(self, queryset=None):
# get the existing object or created a new one
obj, created = Worker.objects.get_or_create(mac=self.kwargs['mac'])
return obj
et voilà merci @chriskief