Question

I have a Workshop Django app. Each Workshop can have multiple attendees via a related WorkshopAttendee model (simplified below). I am using Django's ModelForm in a Class Based View, and in forms.py I am using crispy-forms:

models.py (relevant parts)

class Workshop(models.Model):
    title = models.CharField(max_length=100)
    information = models.TextField()
    location = models.TextField()

class WorkshopAttendee(models.Model):
    workshop = models.ForeignKey(Workshop)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

views.py (relevant parts)

from django.views.generic.edit import FormView
from workshop.forms import WorkshopAttendeeForm

class WorkshopAttendeeFormView(FormView):

    def form_valid(self, form):
        # Clean the data
        form_data = form.cleaned_data
        form.save(commit=False)
        return super(WorkshopAttendeeFormView, self).form_valid(form)

forms.py

from django.forms import ModelForm
from workshop.models import WorkshopAttendee
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Field, \
    Fieldset, Button, Hidden, Submit, Reset
from crispy_forms.bootstrap import FormActions

# Create the form class
class WorkshopAttendeeForm(ModelForm):
    def __init__(self, *args, **kwargs):
        # Crispy form Layouts, Fieldsets, etc
        super(WorkshopAttendeeForm, self).__init__(*args, **kwargs)

    class Meta:
        model = WorkshopAttendee

urls.py (relevant parts)

urlpatterns = patterns('',
    url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[\w\-.]+)/(?P<action>[\w\-.]+)/$',
        WorkshopAttendeeFormView.as_view(
            form_class=WorkshopAttendeeForm,
            success_url="success/",
            template_name='workshop/workshop_registration.html',
        ), name='workshop-attendee-register'
    ),
    url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[\w\-.]+)/$',
        WorkshopDetailView.as_view(
            context_object_name='workshop_detail',
            queryset=Workshop.objects.select_related(),
            template_name='workshop/workshop_detail.html',
        ), name='workshop-detail'
    ),
)

My question is, how can I seed the form with the workshop_id (i.e. the FK relation) in a hidden form field? Obviously because the form hasn't been submitted yet, there is no FK relation yet. But in the URL I have the kwarg of Workshop slug. I can hard-code the workshop_id as a Hidden() crispy-forms field on a per workshop basis, but this is totally unDRY. Any ideas? I don't think I can use the select_related() or prefetch_related() methods on the model in urls.py, so maybe I have to somehow get both models into the form view somehow?

I don't feel like this is an edge-case scenario, and I am sure someone else has had a similar app workflow.

Thanks in advance.

UPDATE

After further research it appears that I can do this, using Django Formsets. Not figured out exactly how yet..... hints welcome.

Was it helpful?

Solution 2

Turns out I was overly complicating this.

All I needed to do was modify the form_valid method for Django's GCBV FormView to this:

workshop/views.py

from django.views.generic.edit import FormView
from workshop.models import Workshop, WorkshopAttendee
from workshop.forms import WorkshopAttendeeForm
from django.http import HttpResponseRedirect

class WorkshopAttendeeFormView(FormView):

    def form_valid(self, form):

        self.object = form.save(commit=False)
        self.object.workshop = Workshop.objects.filter(slug=self.kwargs['slug'])[0]
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

Which basically does not save the form on submit, but instead overrides it and first updates the object it is about to save (the WorkshopAttendee object) with the relevant Workshop (based on the Workshop slug field, which is unique), then saves the updated object (self.object.save) and kicks me to the success url.

Big thanks to @init3 for his helpful pointers. Much appreciated.

OTHER TIPS

You don't need to pass the PK - you got it already in your URL as the slug.

So let's say this is your URL: http://example.com/workshops/awesome-workshop-slug/sign_in/ Your urls.py should look like this:

url(r'^workshop/(?P<workshop_slug>\w+)/sign_in/$',
        # ../workshops/awesome-workshop-slug/sign_in/
        view = 'workshop_signin',
        name = 'workshop-signin',
    ),

Ok, in your views.py you're able to do this:

@login_required
def workshop_signin(request, workshop_slug, template='workshop/sign_in.html'):
    """Register user to workshop."""
    form = WorkshopForm()
    workshop = Workshop.objects.filter(slug=workshop_slug)[0]

    if request.method == 'POST':
        form = WorkshopForm(request.POST, instance=workshop)
        if form.is_valid():
            messages.info(request, 'Yay!')


    kwargs = {
        'workshop_form': form,
    }
    return render_to_response(template, kwargs, context_instance=RequestContext(request))

*untested quick and dirty code

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top