I'm making an app that allows people to set up instructional events that may span multiple days. Here's some of my code:
Models:
class Event(models.Model):
event_name = models.CharField('Event Name', max_length=200)
short_description = models.TextField('Short Description', max_length=140)
class EventDay(models.Model):
event = models.ForeignKey(Event)
day_name = models.CharField(max_length=30)
start_time = models.DateTimeField('Starting Date and Time')
end_time = models.DateTimeField('Estimate Ending Time')
Views
from .models import Event, EventDay
class EventCreate(LoginRequiredMixin, CreateView):
model = Event
fields = ['event_name', 'short_description']
In the admin when I add an event it works perfectly, allowing me to add as many days to the event as I want. But the event add page outside of the admin only displays the fields from Event, not EventDay.
I know my Views code looks a little empty as far as anything relating to the EventDay goes. But, before posting here I tried as many different things as I could think of to try and get fields that work like the admin page. I just left out the mess I tried here. I also couldn't find anything in the docs about where I'm going wrong.
I didn't include my template/url code since I don't think that's the problem. Again I can add events, just not the EventDay part of it. But I'm new to this all so if you need me to post more code I will.
You're looking for inline formset.
CreateView can be used with this but I think you should use a TemplateView (a base class for CreateView) instead, which is simpler to extend/modify.
# forms.py
from django import forms
from .models import Event
class EventForm(forms.ModelForm):
class Meta:
model = Event
# views.py
from django.http import HttpResponseRedirect
from django.views.generic import TemplateView
from django.forms.models import inlineformset_factory
from .models import EventDay, Event
from .forms import EventForm
class EventCreate(TemplateView):
template_name = 'event_create.html'
def get(self, request, *args, **kwargs):
"GET forms ready!"
# get form for Event
event_form = EventForm()
# here's the 'magic' inlineformset, better read the
# django documentation about this
EventDayFormSet = inlineformset_factory(Event, EventDay)
formset = EventDayFormSet()
# add to context and return response
context = {'form': event_form, 'formset': formset}
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
"Handle form submission on POST request"
# get form for Event with POST data
event_form = EventForm(data=request.POST)
# get formset for EventDay with POST data
EventDayFormSet = inlineformset_factory(Event, EventDay)
formset = EventDayFormSet(data=request.POST)
if event_form.is_valid() and formset.is_valid():
# valid forms, OK to save
event = event_form.save()
# EventDay needs a ForeignKey for Event since the field is
# not nullable. Save the forms without committing to database...
eventdays = formset.save(commit=False)
for eventday in eventdays:
# ... and add the ForeignKey field
eventday.event = event
eventday.save()
# TODO use reverse('name_of_the_view_to_redirect_to') instead of '/'
return HttpResponseRedirect('/')
# Some error occurred with the forms, display errors and forms
# so the user can fix it
context = {'form': event_form, 'formset': formset}
return self.render_to_response(context)
To know a bit more about class base views structure and which method to override and the like, checkout Classy Class Based Views.