I've written the following custom formset, but for the life of me I don't know how to save the form. I've searched the Django docs and done extensive searches, but no one solution works. Lots of rabbit holes, but no meat ;-) Can someone point me in the right direction?
// views.py partial //
#login_required
def add_stats(request, group_slug, team_id, game_id, template_name = 'games/stats_add_form.html'):
if request.POST:
formset = AddStatsFormSet(group_slug=group_slug, team_id=team_id, game_id=game_id, data=request.POST)
if formset.is_valid():
formset.save()
return HttpResponseRedirect(reverse('games_game_list'))
else:
formset = TeamStatFormSet(group_slug=group_slug, team_id=team_id, game_id=game_id)
return render_to_response(template_name, {'formset': formset,})
// modles.py partial //
class PlayerStat(models.Model):
game = models.ForeignKey(Game, verbose_name=_(u'sport event'),)
player = models.ForeignKey(Player, verbose_name=_(u'player'),)
stat = models.ForeignKey(Stat, verbose_name=_(u'statistic'),)
total = models.CharField(_(u'total'), max_length=25, blank=True, null=True)
class Meta:
verbose_name = _('player stat')
verbose_name_plural = _('player stats')
db_table = 'dfusion_playerstats'
def __unicode__(self):
return u'%s' % self.player
// forms.py
class TeamStatForm(forms.Form):
total = forms.IntegerField()
class BaseTeamStatsFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.group_slug = kwargs['group_slug']
self.team_id = kwargs['team_id']
self.game_id = kwargs['game_id']
self.extra = len(Stat.objects.filter(group__slug=self.group_slug))
del kwargs['group_slug']
del kwargs['game_id']
del kwargs['team_id']
super(BaseTeamStatsFormSet, self).__init__(*args, **kwargs)
def add_fields(self, form, index):
super(BaseTeamStatsFormSet, self).add_fields(form, index)
form.fields["stat"] = forms.ModelChoiceField(queryset = Stat.objects.filter(group__slug=self.group_slug))
form.fields["game"] = forms.ModelChoiceField(queryset = Game.objects.all())
form.fields["team"] = forms.ModelChoiceField(queryset = Team.objects.all())
form.fields["game"].initial = self.game_id
form.fields["team"].initial = self.team_id
TeamStatFormSet = formset_factory(TeamStatForm, BaseTeamStatsFormSet)
In your custom forms, you'll need to add a save() method that stuffs the form data into your models as needed. All of the data entered in the form will be available in a hash called cleaned_data[].
For example:
def save(self):
teamStat = TeamStat(game_id=self.cleaned_data['game_id'],team_id=self.cleaned_data['team_id'])
teamStat.save()
return teamStat
Only model forms and formsets come with a save() method. Regular forms aren't attached to models, so you have to store the data yourself. How to save a formset? from the Django mailing list has an example of saving data from a regular formset.
Edit: You can always add a save() method to a regular form or formset as gbc suggests. They just don't have one built-in.
I don't see a TeamStat model in your code snippets, but if you had one, your forms.py should look something like this:
class TeamStatForm(forms.ModelForm):
total = forms.IntegerField()
class Meta:
model = TeamStat
class BaseTeamStatsFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
self.group_slug = kwargs['group_slug']
self.team_id = kwargs['team_id']
self.game_id = kwargs['game_id']
self.extra = len(Stat.objects.filter(group__slug=self.group_slug))
del kwargs['group_slug']
del kwargs['game_id']
del kwargs['team_id']
super(BaseTeamStatsFormSet, self).__init__(*args, **kwargs)
def add_fields(self, form, index):
super(BaseTeamStatsFormSet, self).add_fields(form, index)
form.fields["stat"] = forms.ModelChoiceField(queryset = Stat.objects.filter(group__slug=self.group_slug))
form.fields["game"] = forms.ModelChoiceField(queryset = Game.objects.all())
form.fields["team"] = forms.ModelChoiceField(queryset = Team.objects.all())
form.fields["game"].initial = self.game_id
form.fields["team"].initial = self.team_id
TeamStatFormSet = modelformset_factory(TeamStatForm, BaseTeamStatsFormSet)
See Creating forms from models from the Django docs
Related
I have a question for you. I have the following Model:
class Centro_di_costo(models.Model):
centro_di_costo = models.CharField('Centro di costo', max_length=30)
def __str__(self):
return self.centro_di_costo
class AltriCosti(models.Model):
STATUS_CHOICES= [
('VARIABILE', 'VARIABILE'),
('FISSO', 'FISSO'),
]
centro_di_costo = models.ForeignKey(Centro_di_costo)
sub_centro_di_costo = models.CharField('Categoria', max_length=30)
status = models.CharField(choices=STATUS_CHOICES)
price=models.DecimalField()
quantity=models.IntegerField()
I use it in a lot of view, but in one of them I wanna set the value without passing from the POST request.
So I have tried to set the ModelForm in the following manner:
class ModCollaboratori(forms.ModelForm):
class Meta:
model = AltriCosti
fields = "__all__"
def __init__(self, *args, **kwargs):
super(ModCollaboratori, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_show_labels = False
self.fields['centro_di_costo'].value= "Servizi di Produzione"
self.fields['sub_centro_di_costo'].value = "Collaboratori esterni"
self.fields['status'].value = "VARIABILE"
But It does not work. How could I fix the code to work?
You can exclude fields from your form:
class ModCollaboratori(forms.ModelForm):
class Meta:
model = AltriCosti
exclude = ['centro_di_costo', 'sub_centro_di_costo', 'status']
Then in your view you can "inject" value for these fields:
def some_view(request):
if request.method == 'POST':
form = ModCollaboratori(request.POST, request.FILES)
if form.is_valid():
form.instance.sub_centro_di_costo = 'Collaboratori esterni'
form.instance.status = 'VARIABILE'
form.instance.centro_di_costo = Centro_di_costo.objects.get_or_create(
centro_di_costo='Servizi di Produzione'
)[0]
form.save()
return redirect('name-of-some-view')
else:
form = ModCollaboratori()
return render(request, 'some_template.html', {'form': form})
for your code
self.fields['status'].value = "VARIABLE"
to make it work change to
self.instance.status = "VARIABLE"
Result:
Status: VARIABLE
basically ModelForm.__init__() will populate instance values into form.
but if we add extra field to this form, we will need to populate it by ourself in kwargs["initial"],
because this field not include in the model.
class SomeForm(forms.ModelForm):
custom_field = forms.CharField()
def __init__(self, *args, **kwargs):
kwargs["initial"]["custom_field"] = "xxxxx"
super().__init__(*args, **kwargs)
I tried this in my modelform:
class Ledgerform(forms.ModelForm):
class Meta:
model = ledger1
fields = ('name', 'group1_Name')
def __init__(self, User, Company, *args, **kwargs):
self.User = kwargs.pop('User', None)
self.Company = kwargs.pop('Company', None)
super(Ledgerform, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs = {'class': 'form-control',}
self.fields['group1_Name'].queryset = group1.objects.filter(User= self.User,Company = self.Company)
In my views.py I have done something like this:
class ledger1ListView(LoginRequiredMixin,ListView):
model = ledger1
paginate_by = 15
def get_queryset(self):
return ledger1.objects.filter(User=self.request.user, Company=self.kwargs['pk'])
class ledger1CreateView(LoginRequiredMixin,CreateView):
form_class = Ledgerform
def form_valid(self, form):
form.instance.User = self.request.user
c = company.objects.get(pk=self.kwargs['pk'])
form.instance.Company = c
return super(ledger1CreateView, self).form_valid(form)
I want to perform the the same query that I have passed in my ledger1ListView by using queryset in my modelform but my kwargs.pop is not returning the current user or the company...
This is my models.py:
class ledger1(models.Model):
User = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,null=True,blank=True)
Company = models.ForeignKey(company,on_delete=models.CASCADE,null=True,blank=True,related_name='Companys')
name = models.CharField(max_length=32)
group1_Name = models.ForeignKey(group1,on_delete=models.CASCADE,blank=True,null=True)
Do any one know what I am doing wrong in my code?
Thank you in advance
You can override the FormMixin.get_form_kwargs [Django-doc] in your view, that it constructs a dictionary with the parameters necessary to initialize the form, like:
class ledger1CreateView(LoginRequiredMixin,CreateView):
form_class = Ledgerform
def get_form_kwargs(self):
data = super(ledger1CreateView, self).get_form_kwargs()
data.update(
User=self.request.User,
Company=company.objects.get(pk=self.kwargs['pk'])
)
return data
The form_valid function is called after the form is constructed, validated and appears to be valid. Typically it is used to redirect the user to the "success page".
I need to assign multiple CampaignTypes to a Campaign unsing Django FormsModels.
Selecting many CapaignTypes at once, adding the CapaignTypes to only one campaign. Thanks I will appreciate any help
class Campaign(models.Model):
client_id = models.ForeignKey(Company)
name = models.CharField(max_length=45, null=True)
campaign_status = models.ForeignKey(CampaignStatus)
def __str__(self):
return self.name
class Campaign_type(models.Model):
campaign_type = models.CharField(max_length=45)
client_id = models.ForeignKey(Company)
campaign_id = models.ManyToManyField(Campaign, verbose_name='Campaign(s)')
def __str__(self):
return self.campaign_type + ' ' + str(self.client_id)
My code in form.py
class CampaignCampaignTypeForm(forms.ModelForm):
class Meta:
model = CampaignType
exclude = ['campaign_id', 'client_id']
campaign_type = forms.ModelMultipleChoiceField(queryset=CampaignType.objects.all())
def __init__(self, *args, **kwargs):
company = kwargs.pop("company")
if kwargs.get('instance'):
initial = kwargs.setdefault('initial', {})
initial['campaign_type'] = [t.pk for t in kwargs['instance'].campaing_type_set.all()]
forms.ModelForm.__init__(self, *args, **kwargs)
My code in view.py
def add_campaign_type_to_campaign(request, campaign_id):
if not request.user.is_authenticated():
return render(request, 'campaign/login.html')
else:
client_user = ClientUser.objects.get(client=request.user.pk)
form = CampaignCampaignTypeForm(data=request.POST or None, company=client_user.company)
if form.is_valid():
campaigntype = form.save(commit=False).clean()
#client_user = ClientUser.objects.get(client=request.user.pk)
campaign = Campaign.objects.get(id=campaign_id)
campaigntype.campaign_id = campaign
campaigntype.save()
form.save_m2m()
# return render(request, 'campaign/detail_campaign.html', {'campaign_type': campaign_type})
context = {
"form": form,
}
Do you try forms.SelectMultiple widget? Or if you can using Bootstrap on frontend, Select2 is a good JS package to help you on multiple selection.
I have a custom form that is not saving to the database. I do not get any errors but the values do not save to the database. Any ideas?
views.py
def diseasestateoption(request, disease_id, state_id):
state = get_object_or_404(State, pk=state_id)
disease = get_object_or_404(Disease, pk=disease_id)
if request.method == "POST":
form = UpdateStateWithOptionsForm(request.POST, instance=state)
if form.is_valid():
for option_id in request.POST.getlist('options'):
state_option = StateOption.objects.create(partstate=state, partoption_id=int(option_id))
state_option.save()
return HttpResponseRedirect(reverse('success'))
else:
form = UpdateStateWithOptionsForm(instance=state)
models.py
class Option(models.Model):
relevantdisease = models.ForeignKey(Disease)
option = models.CharField(max_length=300)
class State(models.Model):
state = models.CharField(max_length=300, verbose_name='state')
relevantdisease = models.ForeignKey(Disease, verbose_name="disease")
relevantoption = models.ManyToManyField(Option, through='StateOption')
class StateOption(models.Model):
parttstate = models.ForeignKey(State)
partoption = models.ForeignKey(Option)
forms.py
class UpdateStateWithOptionsForm(forms.ModelForm):
class Meta:
model = State
exclude = ['state', 'relevantdisease']
def __init__(self, *args, **kwargs):
super(UpdateStateWithOptionsForm, self).__init__(*args, **kwargs)
self.fields['relevantoption']=forms.ModelMultipleChoiceField(queryset=Option.objects.all(),required=True, widget=forms.CheckboxSelectMultiple)
I think Problem is with getting option from POST, use-
request.POST.getlist('relevantoption')
in stead of
request.POST.getlist('options')
apart, why to use form here for single multiple choice field, even where you are modifying choices also and not using form.save too.
I'm stuck trying to save an instance of a model that gets from a form an instance of another model as foreign key.
Models
class Customer(models.Model):
owner = models.ForeignKey(User)
custname = models.CharField()
class Appointment(models.Model):
user = models.ForeignKey(User)
start = models.DateTimeField()
end = models.DateTimeField()
customer = models.ForeignKey(Customer)
Form
class AppointmentForm(forms.Form):
basedate = forms.DateField()
start = forms.TimeField(widget=forms.Select())
end = forms.IntegerField(widget=forms.Select())
customer = forms.ModelMultipleChoiceField(queryset=Customer.objects.all())
The method that I'm not able to get working in a generic FormView:
def form_valid(self, form):
if form.is_valid():
appointment = Appointment()
appointment.user = self.request.user
basedate = form.cleaned_data['basedate']
start = form.cleaned_data['start']
duration = form.cleaned_data['end']
appointment.start = datetime.datetime.combine(basedate, start)
appointment.end = appointment.start + datetime.timedelta(minutes=duration)
appointment.save()
return super(AppointmentCreate, self).form_valid(form)
What should I add in the last method to read the foreign key customer from the form, and therefore pass it to the appointment? And is there any way of filtering so that in the form only appear customers belonging to the request.user?
Many thanks in advance for your help.
Something like this should work. A couple of things:
1) I changed the form field to a ModelChoiceField instead of multiple choice. You'll want to use a ModelChoiceField to show the relationship. I changed this from MultipleChoice since, according to your model, you only want to save one choice. You can read more on ModelChoiceFields here: https://docs.djangoproject.com/en/dev/ref/forms/fields/
2) In your forms, I changed the choice query to customer = forms.ModelChoiceField(queryset=Customer.objects.filter(owner=request.user). This will filter for Customers of the specific user only.
forms.py
class AppointmentForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(AppointmentForm, self).__init__(*args, **kwargs)
basedate = forms.DateField()
start = forms.TimeField(widget=forms.Select())
end = forms.IntegerField(widget=forms.Select())
customer = forms.ModelChoiceField(queryset=Customer.objects.filter(owner=request.user))
views.py
def form_valid(self, form):
if request.method=='POST':
form = AppointmentForm(request.POST, request=request)
if form.is_valid():
appointment = Appointment()
appointment.user = self.request.user
basedate = form.cleaned_data['basedate']
start = form.cleaned_data['start']
duration = form.cleaned_data['end']
appointment.customer = form.cleaned_data['customer']
appointment.start = datetime.datetime.combine(basedate, start)
appointment.end = appointment.start + datetime.timedelta(minutes=duration)
appointment.save()
return super(AppointmentCreate, self).form_valid(form)
else:
form = AppointmentForm()
Finally I did it. The key was to override the get method of FormView class in views.py, rather than modifying the init in forms.py:
forms.py:
class AppointmentForm(forms.Form):
basedate = forms.DateField()
start = forms.TimeField(widget=forms.Select())
end = forms.IntegerField(widget=forms.Select())
customer = forms.ModelChoiceField(queryset=Customer.objects.all())
...
views.py:
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates a blank version of the form.
"""
choices_start, choices_duration = self._get_choices()
form_class = self.get_form_class()
form = self.get_form(form_class)
form.fields['start'].widget=forms.Select(choices=choices_start)
form.fields['end'].widget=forms.Select(choices=choices_duration)
form.fields['customer'].queryset=Customer.objects.filter(owner=request.user)
return self.render_to_response(self.get_context_data(form=form))
#Dan: Many thanks for your effort in helping me out.