Django: URL-Parameter in CBV CreateView

Django, Flask, Bottle, WSGI, CGI…
Antworten
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ich habe ein (Verständnis-) Problem bei Djangos CBV. Und zwar würde ich gerne ich gerne für eine CreateView ein zusätzliches Parameter in der URL mit übergeben. Anhand des Parameters soll dann das Model festgelegt werden.

Kleines Beispiel:

models.py

Code: Alles auswählen

from django.db import models

class Customer(models.Model):
    customer = models.CharField('Customer Name')

class FB(models.Model):
    for_customer = models.ForeignKey('Customer')
    make = models.CharField('Manufacturer')

class Contact(models.Model):
    for_customer = models.ForeignKey('Customer')
    name = models.CharField('Name')
    tel_no = models.IntegerField('Phone Number')
urls.py:

Code: Alles auswählen

from django.conf.urls import url
from whw import views

urlpatterns = [
    ...
    url(r'(?P<pk>\d{1,5})/add/(?P<detail>(customer|fb>))/$', views.DetailCreate.as_view(), name='add-detail'),
]

views.py:

Code: Alles auswählen

...
from django.views.generic.edit import CreateView
from whw.models import Contact, FB

...

class DetailCreate(CreateView):
    if detail == 'fb':
        model = FB
        fields = ['make']
    elif detail == 'contact':
        model = Contact
        fields = ['name', 'tel_no']
    template = 'detail_form.html'

Als Fehlermeldung kommt dann, dass `detail` in `DetailCreate` nicht bekannt ist (was ja auch irgendwie logisch ist...)

Frage: Wie komme da dran? Mir ist nicht klar, wie ich auf die URL-Parameter innerhalb der Klasse zugreifen kann. Oder geht das nicht in einer CBV?
Ich gehe auch davon aus, dass das irgendwo dokumentiert ist. Nur finde ich das nicht (oder ich hab's gefunden und nicht verstanden...)

Gruß, noisefloor
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Für jeden Request der ein CBV erreicht wird eine Instanz der Klasse erstellt und die behandelt den Request. Die Klasse selbst hat also überhaupt keine Ahnung was ein Request ist. Ein Klassenattribut kann also auch nicht vom Request abhängig sein.

Die gute Nachricht ist dass `model` soweit ich sehen kann kein Klassenattribut sein muss, ein `model` Instanzattribut dürfte es auch tun. Am sinnvollsten ist wahrscheinlich sich darum in `dispatch()` zu kümmern weil diese Methode `get()`, `post()` usw. aufruft und du `model` wahrscheinlich in all diesen Methoden gesetzt haben willst.
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

das war der Hinweis, den ich brauchte: dispatch() überschreiben bzw. erweitern ist der (ein?) Weg, wie es funktioniert (thx an DasIch :-) ).

Hier mal die neue + funktionierende Version `views.py`:

Code: Alles auswählen

class DetailCreate(CreateView):
    
    template_name = 'whw/detail_form.html'
    
    def dispatch(self, *args, **kwargs):
        self.detail = kwargs['detail']
        self.for_customer = kwargs['pk']
        if self.detail == 'contact':
            self.model = Contact
            self.fields = ['name', 'tel_no']
        elif self.detail == 'fb':
            self.model = FB
            self.fields = ['make']
        self.fields.append('for_customer')
        return super(DetailCreate, self).dispatch(*args, **kwargs)
        
    def get_context_data(self, **kwargs):
        context = super(DetailCreate, self).get_context_data(**kwargs)
        context['for_customer'] = self.for_customer
        context['detail'] = self.detail
        return context
    
    def get_form(self, form_class=None):
        form = super(DetailCreate, self).get_form()
        form.fields['for_customer'].widget = HiddenInput()    
        return form

    def get_initial(self):
        initial = super(DetailCreate, self).get_initial()
        initial['for_customer'] = self.for_customer
        return initial
In der `urls.py` in 1. Post ist auch ein kleiner Fehler: in der 2. RegEx muss `contact` stehen, nicht `customer`.

Und der Vollständigkeit halber noch das Template:

Code: Alles auswählen

{% extends 'whw/base.html' %}
{% block content %}
<h1>Add Details</h1>
<p>Add a new detail to the database:
<form action="{% url 'whw:add-detail' pk=for_customer detail=detail %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <p><input type="submit" value="Save" />
</form>
{% endblock content %}
So funktioniert es, wie ich mir das vorgestellt habe. Wenn sonst noch jemand Ideen, Verbesserungsvorschläge hat -> her damit :-)
Antworten