Reputation: 3418
Let's say that we model university programs, which are divided in tracks. A track belongs to a program. So:
class Program(models.Model):
name = models.CharField(max_length=30)
class Track(models.Model):
name = models.CharField(max_length=30)
program = models.ForeignKey(Program, on_delete=models.CASCADE, related_name='tracks')
I'd like to allow the creation of a track, with the possibility of specifying the program it belongs to, so I can use:
class TrackCreateView(CreateView):
model = Track
fields = ['name', 'program']
But from the program details, I want to allow the creation of a track for that specific program, so I came up with:
class ProgramTrackCreateView(CreateView):
model = Track
fields = ['name']
def form_valid(self, form):
form.instance.program_id = self.kwargs['pk']
return super().form_valid(form)
In the urls.py
, a pk
parameter is provided, so this will populate the program_id
correctly.
Is there any way to merge these two views into a single one, e.g. passing a parameter to the .to_view()
method, or something similar? Because I need to change the list of fields to be displayed before the form is created and I need to know if I need to override program_id
or not.
Upvotes: 1
Views: 58
Reputation: 21787
One way would be to override your views get_form
method and dynamically remove your forms field in case the pk
is passed to the view:
class ProgramTrackCreateView(CreateView):
model = Track
fields = ['name', 'program']
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
program_id = self.kwargs.get('pk')
if program_id is not None:
del form.fields['program']
form.instance.program_id = program_id
return form
Upvotes: 1
Reputation: 7330
You can make use of the django forms and override the form's __init__
method.
from django import forms
class TaskCreateForm(forms.ModelForm):
class Meta:
fields = ['name', 'program']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs.get('pk'):
form.fields['program'].widget = forms.HiddenInput()
Now in your view:
class ProgramTrackCreateView(CreateView):
model = Track
form_class = TaskCreateForm
success_url = reverse_lazy('url')
def form_valid(self, form):
if self.kwargs.get('pk):
form.instance.program_id = self.kwargs['pk']
form.save()
return super().form_valid(form)
Upvotes: 1
Reputation: 603
Make a FormView for this, in the init function of it you can then check against whatever context you decide to use.
def MyFormView(forms.Form):
fields_for_all_options = forms.CharField(label='SomeLabel', max_length=200)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if case x:
self.fields['somefield'] = forms.CharField(label= 'a different label', max_length=200)
Alternatively you could also pass all fields available on the form then hide them in the frontend
Upvotes: 0