ModelFormset in Django CreateView - python

I'm still new to Django & I would like to know how can allow user to add more than 1 ReferrerMember on Registration form as I wanted to achieve similar to the image url below
https://imgur.com/a/2HJug5G
I applied modelformset but so far it's giving me an error where "membership_id" violates not-null constraint the moment I submitted the form.
I've searched almost everywhere to find how to implement this properly especially on class-based view instead of function based view but still no luck. If possible please help me point out on any mistakes I did or any useful resources I can refer to
models.py
class RegisterMember(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField()
class ReferrerMember(models.Model):
contact_name = models.CharField(max_length=100)
company_name = models.CharField(max_length=100)
membership = models.ForeignKey(RegisterMember, on_delete=models.CASCADE)
forms.py
class RegisterMemberForm(ModelForm):
class Meta:
model = RegisterMember
fields = ['name', 'email', ]
class ReferrerForm(ModelForm):
class Meta:
model = ReferrerMember
fields = ['contact_name ', 'company_name ', ]
ReferrerMemberFormset = modelformset_factory(ReferrerMember, form=RegisterMemberForm, fields=['contact_name ', 'company_name ', ], max_num=2, validate_max=True, extra=2)
views.py
class RegisterMemberView(CreateView):
form_class = RegisterMemberForm
template_name = 'register.html'
def post(self, request, *args, **kwargs):
member_formset = ReferrerMemberFormset (request.POST, queryset=ReferrerMember.objects.none())
if member_formset .is_valid():
return self.form_valid(member_formset )
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['member_formset'] = ReferrerMember(queryset=ReferrerMember.objects.none())
return context
register.html
<form method="post">
{% csrf_token %}
{{form.as_p}}
{{member_formset.as_p}}
<input type="submit">
</form>

Related

Django form with dropdown list using Database returns empty fields

I'm discovering Django and I'm trying to develop a simple application.
I have three tables in my database :
One big table to report all the information to users and 2 tables to create drop down list on my form (but no usage of foreign keys on purpose). I need to have these two tables because statuses and areas need to be editable at all time and need to be written in the same way each time in the main table Action.
Here is my model.py :
class Status(models.Model):
id_status = models.AutoField(db_column='ID_STATUS', primary_key=True)
status = models.CharField(db_column='STATUS', max_length=50)
def __str__(self):
return self.status
class Meta:
managed = False
db_table = 'T_EAVP_STATUS'
class Area(models.Model):
id_area = models.AutoField(db_column='ID_AREA', primary_key=True)
area_name = models.CharField(db_column='AREA_NAME', max_length=50)
def __str__(self):
return self.area_name
class Meta:
managed = False
db_table = 'T_EAVP_AREA'
class Action(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
title = models.CharField(db_column='TITLE', max_length=200)
due_date = models.DateTimeField(db_column='DUE_DATE')
status = models.CharField(db_column='STATUS', max_length=50)
date_insert = models.DateTimeField(db_column='DATE_INSERT', auto_now_add=True)
emitting_area = models.CharField(db_column='EMITTING_AREA', max_length=50)
receiving_area = models.CharField(db_column='RECEIVING_AREA', max_length=50)
owner = models.CharField(db_column='OWNER', max_length=200)
def __str__(self):
return self.title
class Meta:
managed = False
db_table = 'T_EAVP_ACTION'
Here is my forms.py :
class ActionForm(forms.ModelForm):
status = forms.ModelChoiceField(queryset=Status.objects.all())
receiving_area = forms.ModelChoiceField(queryset=Area.objects.all())
emitting_area = forms.ModelChoiceField(queryset=Area.objects.all())
class Meta:
model = Action
fields = ['title', 'due_date', 'owner']
Here is my views.py :
#csrf_exempt
#xframe_options_exempt
def action_add(request):
if request.method == 'POST':
form = ActionForm(request.POST)
if form.is_valid():
action = form.save()
return redirect('action-list')
else:
form = ActionForm()
return render(request, 'polls/action_add.html', {'form': form})
Here is my HTML code :
{% extends 'polls/base.html' %}
{% block title %}{{ action.title }}{% endblock %}
{% block content %}
<h1>Ajouter une action</h1>
<form action="" method="post" class="form" novalidation>
{% csrf_token %}
{{ form }}
<br><br>
<input type="submit" class="submit" value="Créer">
</form>
{% endblock %}
I'm pretty sure I've imported all the needed libraries from Django.
Problem : When I'm running my code and I try to create a new action using my ActionForm, it is not working properly.
In the Action table, the fields that correspond to the fields filled by the drop down lists are completely empty.
Table Status and table Area contain values so the problem is not coming from here.
I've tried a lot of different things but nothing seems to work and fields are always empty in my database after the save of the form.
If someone sees a solution, I'm interested !
The solution was to add the name of the fields in form.py :
class ActionForm(forms.ModelForm): # Crée un formulaire se basant sur Action
status = forms.ModelChoiceField(queryset=Status.objects.all())
receiving_area = forms.ModelChoiceField(queryset=Area.objects.all())
emitting_area = forms.ModelChoiceField(queryset=Area.objects.all())
class Meta:
model = Action
fields = ['title', 'due_date', 'owner', 'status', 'receiving_area', 'emitting_area']
Thank you Willem Van Onsem !

Django Form Dynamic Fields looping over each field from POST and creating records

I'm looking for some advice where to go from here. I've been working on making a Form, which dynamically generates its fields.
The form is working and generating everything correctly. However, I am having issues with how to save the actual form data. I'm looking for each field to save as a new item in a model.
The View Class from view.py
class MaintenanceCheckListForm(LoginRequiredMixin, FormView):
login_url = '/accounts/login'
template_name = 'maintenance/checklist.html'
form_class = MaintenanceCheckListForm
success_url = reverse_lazy('m-checklist')
def form_valid(self, form):
form.cleaned_data
for key, values in form:
MaintenanceCheckList.objects.create(
item = key,
is_compliant = values
)
return super().form_valid(form)
The Form from forms.py
class MaintenanceCheckListForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MaintenanceCheckListForm, self).__init__(*args, **kwargs)
items = Maintenance_Item.objects.all()
CHOICES = (
('P','Compliant'),
('F','Non-Compliant'),
)
for item in items:
self.fields[str(item.name)] = forms.ChoiceField(
label=item.name,
choices=CHOICES,
widget=forms.RadioSelect,
initial='F',
)
The Model, from models.py
class MaintenanceCheckList(CommonInfo):
CHOICES = (
('P','Compliant'),
('F','Non-Compliant'),
)
id = models.AutoField(primary_key=True)
item = models.CharField(max_length=100)
is_compliant = models.CharField(max_length=20, choices= CHOICES)
I am having trouble accessing the data from the Form when it POST's. I've done some troubleshooting where I have set the values statically in the '''form_valid''' and it appears to generate the correct amounts of entires in the model. However the trouble begins when I attempt to insert the values from the POST.
I receieve the below error, which I believe it is trying to dump all the keys and values into a single item instead of looping over each key, value and creating the item.
DataError at /maintenance/checklist
value too long for type character varying(100)
Request Method: POST
Request URL: http://t1.localhost:8000/maintenance/checklist
Django Version: 3.1.6
Exception Type: DataError
Exception Value:
value too long for type character varying(100)
I'm fairly new to the world of Django (4 weeks and counting so far, and maybe 12 weeks into python). So any assistance would be amazing!
I believe you have somewhat gone on a tangent. There's a simpler solution of using Model formsets for what you want.
First if you want a custom form make that:
from django import forms
class MaintenanceCheckListComplianceForm(forms.ModelForm):
item = forms.CharField(widget = forms.HiddenInput())
is_compliant = forms.ChoiceField(
choices=MaintenanceCheckList.CHOICES,
widget=forms.RadioSelect,
initial='F',
)
class Meta:
model = MaintenanceCheckList
fields = ('item', 'is_compliant')
Next use it along with modelformset_factory in your views:
from django.forms import modelformset_factory
class MaintenanceCheckListFormView(LoginRequiredMixin, FormView): # Changed view name was a bit misleading
login_url = '/accounts/login'
template_name = 'maintenance/checklist.html'
success_url = reverse_lazy('m-checklist')
def form_valid(self, form):
instances = form.save()
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['queryset'] = MaintenanceCheckList.objects.none()
kwargs['initial'] = [{'item': obj['name'], 'is_compliant': 'F'} for obj in Maintenance_Item.objects.all().values('name')]
return kwargs
def get_form(self, form_class=None):
kwargs = self.get_form_kwargs()
extra = len(kwargs['initial'])
form_class = modelformset_factory(MaintenanceCheckList, form=MaintenanceCheckListComplianceForm, extra=extra)
return form_class(**kwargs)
Now in your template:
<form method="post">
{{ form }}
</form>
Or manually render it:
<form method="post">
{{ form.management_form }}
{% for sub_form in form %}
Item: {{ sub_form.item.value }}
{{ sub_form }}
{% endfor %}
</form>
Note: The above usage is a bit weird due to the naming of the formset variable as form by the FormView you should look into improving that a bit.
Note: Looking at the implementation it feels a bit weird to do this. I would advice you to redesign your models a bit. Perhaps a foreign key between your models? It basically feels like you have duplicate data with this implementation.

Django: UpdateView and ModelForm

I'm having a weird issue when using a ModelForm as a form_class for UpdateView.
First of all: When using the UpdateView without the form_class tag, everything works perfectly. However, when I try to use the ModelForm (because I want to add a MarkdownField) I get <mediwiki.views.MediwikiForm object at 0x7f990dfce080>displayed in my browser window. Just in plain text?
#template/mediwiki/create2.html:
<form action="" method="post">{% csrf_token %}
{{ form }}
<input type="submit" value="Save" />
</form>
#views.py:
class EntryUpdate(UpdateView):
model = Mediwiki
slug_field = 'non_proprietary_name'
template_name = "mediwiki/create2.html"
form_class = MediwikiForm⋅
#fields = '__all__' #this works...
#forms.py
class MediwikiForm(ModelForm):
# wiki_page_markdown = MarkdownxFormField()
class Meta:
model = Mediwiki⋅
fields = ['non_proprietary_name', 'category', 'wiki_page_markdown']
#models.py
class Mediwiki(models.Model):
non_proprietary_name = models.CharField(max_length = 100, unique = True)
category = models.ManyToManyField(Category)
wiki_page = models.TextField(blank = True)
wiki_page_markdown = models.TextField(blank = True)
def save(self):
import markdown
self.wiki_page = markdown.markdown(self.wiki_page_markdown)
super(Mediwiki, self).save() # Call the "real" save() method.
def get_absolute_url(self): # For redirect after UpdateView
return reverse('entry', kwargs={'slug': self.non_proprietary_name})
def __str__(self):
return self.non_proprietary_name
#urls.py
url(r'^mediwiki/(?P<slug>\D+)/edit$', EntryUpdate.as_view(), name="update"),
Any idea what might cause this error? Any help will be much appreciated...

2 fields from different models in a form

I have a form that works perfectly fine
models.py:
class Location(models.Model):
title = models.CharField(max_length=300)
description = models.TextField(null=True, blank=True)
address = models.TextField(null=True, blank=True)
class Review (models.Model):
location = models.ForeignKey(Location)
description = models.TextField(null=True, blank=True)
views.py:
class Create(CreateView):
model = coremodels.Review
template_name = 'location/test.html'
fields = '__all__'
def form_valid(self, form):
form.save()
return HttpResponseRedirect('')
return super(Create, self).form_valid(form)
html:
<form action="" method="post">{% csrf_token %}
{{ form}}
<input type="submit" value="Create" />
</form>
When I open the site I can select a location and give a review over the create button. However, now I am dependent on the prefilled values in the Location class. What if I want that a user can directly create a description as well as a title of the location (I don't want the title to be in class Review) I already tried looking for this in the docs but couldn't find anything. Somewhere I read that I could create two different forms that handle to different things but I'm not sure how to merge that all in the class Create. Is there something like model = coremodels.Review & coremodels.Location and then in the html I could do
{{form.title}}
{{form.description}}
Anyone any ideas or search terms I could look for?
Thanks !
EDIT
Ok thanks to Ruddra and this post , here is the working solution. I had to edit it a little in order to get it working for me,
class SomeForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SomeForm, self).__init__(*args, **kwargs)
self.fields['title'] = forms.CharField(label='Title', required = False)
self.fields['description'] = forms.CharField(label='Description', required = False)
self.fields['location'] = forms.ModelChoiceField(queryset= Location.objects.all(), required = False) # This line is for making location not required in form field for input
class Meta:
model = Review
fields = '__all__'
def save(self, commit=True):
"""
It will save location from choice field or inputs of title and description
"""
instance = super(SomeForm, self).save(commit=False)
if instance.location_id:
instance.save()
else:
new_location = Location.objects.create(title=self.cleaned_data['title'])
instance.location = new_location
instance.save()
return instance
and the views.py
class Create(CreateView):
model = coremodels.Review
template_name = 'location/test.html'
form_class = SomeForm
Unfortunately you can't use two models like this, you have to write a form and do the stuffs there. For example:
class SomeForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'] = forms.CharField(label='Title', required = False)
self.fields['description'] = forms.CharField(label='Description', required = False)
self.fields['location'] = forms.ModelChoiceField(queryset= Location.objects.all(), required = False) # This line is for making location not required in form field for input
class Meta:
model = Review
fields = '__all__'
def save(self, commit=True):
"""
It will save location from choice field or inputs of title and description
"""
instance = super().save(commit=False)
if instance.location:
instance.save()
else:
new_location = Location.objects.create(title=self.cleaned_data['title'], description = self.cleaned_data['description']])
instance.location = new_location
instance.save()
return instance
Use it in view:
class Create(CreateView):
model = coremodels.Review
template_name = 'location/test.html'
form = SomeForm
And you have make sure that, location is nullable or not required in form (I have added that to the example)

Trouble with Django ModelChoiceField

Hi bit of a beginner question about using django's modelchoicefield in a form I'm building.
I just need get django to display a drop down list of ingredients in a form. I've gotten to the point where the page renders but the form does not, I was getting errors before so I am kind of perplexed at the moment. I was hoping for some guidance.
Using python 2.7.6 and django 1.6.2. If I left anything out let me know.
Thanks!
Code is below:
views:
args = {}
#add csrf sercurity
args.update(csrf(request))
args['form'] = form
return render_to_response('newMeal.html', args)
form:
from django import forms
from models import meals, ingredients, recipe
class mealForm(forms.ModelForm):
breakfast = forms.ModelChoiceField(queryset=recipe.objects.all())
# Lunch = forms.ModelChoiceField(queryset=recipe.objects.all())
# Dinner = forms.ModelChoiceField(queryset=recipe.objects.all())
class Meta:
model = meals
fields = ('Breakfast','Lunch','Dinner','servingDate')
class recipeForm(forms.ModelForm):
class Meta:
model = recipe
fields = ('Name', 'Directions')
template:
{% extends "base.html" %}
{% block content %}
<p>New Meals go here!</p>
<form action="/meals/newmeal/" method="post">{% csrf_token %}
<table class="selection">
{{form.as_table}}
<tr><td colspan="2"><input type="submit" name="submit" value="Add Meal"></td></tr>
</table>
</form>
{% endblock %}
Model;
from django.db import models
import datetime
Create your models here.
class recipe(models.Model):
Name = models.CharField(max_length=200)
Directions = models.TextField()
pub_date = models.DateTimeField(auto_now_add = True)
def __unicode__(self):
return (self.id, self.Name)
class ingredients(models.Model):
Name = models.CharField(max_length=200)
Quantity = models.IntegerField(default=0)
Units = models.CharField(max_length=10)
Recipe = models.ForeignKey(recipe)
def __unicode__(self):
return self.Name
class meals(models.Model):
Breakfast = models.CharField(max_length=200)
Lunch = models.CharField(max_length=200)
Dinner = models.CharField(max_length=200)
servingDate = models.DateTimeField('date published')
did you import the mealForm:
some thing like :from app.forms import mealForm
form is a function. so try:
args['form'] = mealForm()
Note: don't use render_to_response. it is old use render instead(so don't even need csrf)::
from django.shortcuts import render
def...(request):
....
return render(request,'newMeal.html', {'form': mealForm()})

Categories