Django Formset Not Cleaning/Saving Properly - django

I'm using inlineformset_factory to create a formset. The parent object is a featured set and the child objects are featured items. I'm using the 'django-dynamic-formset' jQuery plugin on the front end to add/remove formset forms dynamically.
While each form in the formset is getting submitted with the proper data as expected, cleaned_data only contains the form's id.
View:
#login_required
#csrf_protect
def edit_featured_set(request, fset_id):
'''
Edit a featured set and its items
'''
c = {}
c.update(csrf(request))
# get existing featured set for this id (if exists)
if fset_id and int(fset_id) > 0:
try:
fset_obj = Featureditemset.objects.get(id=fset_id)
except Featureditemset.DoesNotExist:
# bad or no id passed
return HttpResponse('ObjectDoesNotExist')
else:
# get new featured set object
fset_obj = Featureditemset(id = get_next_auto_inc(Featureditemset))
# define formset to work with featured items
FeaturedItemFormSet = inlineformset_factory(Featureditemset, Featureditem, extra=1, form=FeatureditemEditForm)
if request.method == 'POST':
form = FeatureditemsetEditForm(request.POST, request.FILES, instance=fset_obj, prefix='set')
if form.is_valid():
featured_item_formset = FeaturedItemFormSet(request.POST, request.FILES, instance=fset_obj, prefix='item')
if featured_item_formset.is_valid():
# save form
form.save()
# save formset
print 'Formset: %s' % str(featured_item_formset)
print 'cleaned_data: %s' % featured_item_formset.cleaned_data
featured_item_formset.save()
return HttpResponse('valid')
else:
form = FeatureditemsetEditForm(instance=fset_obj, prefix='set')
featured_item_formset = FeaturedItemFormSet(instance=fset_obj, prefix='item')
c['form'] = form
c['formset'] = featured_item_formset
return render_to_response('admin/edit_featured.html', c, context_instance = RequestContext(request))
Forms:
class FeatureditemEditForm(ModelForm):
class Meta:
model = Featureditem
fields = ('id','img', 'name', 'folio')
class FeatureditemsetEditForm(ModelForm):
class Meta:
model = Featureditemset
fields = ('name', 'application')
Models:
class Featureditemset(models.Model):
id = models.BigIntegerField(primary_key=True, db_column='FEATUREDITEMSET_ID')
application = models.ForeignKey(Application, null=True, db_column='FEATUREDITEMSET_APPLICATION_ID', blank=True)
htmlcontent = models.TextField(db_column='FEATUREDITEMSET_HTMLCONTENT', blank=True)
modified = models.DateTimeField(db_column='FEATUREDITEMSET_MODIFIED', auto_now=True)
viewer = models.ForeignKey(Viewer, null=True, db_column='FEATUREDITEMSET_VIEWER', blank=True)
name = models.CharField(max_length=192, db_column='FEATUREDITEMSET_NAME', blank=True)
class Meta:
db_table = u'featureditemset'
def __unicode__(self):
return str(self.name)
class Featureditem(models.Model):
id = models.BigIntegerField(db_column='FEATURED_ITEM_ID', primary_key=True)
img = models.FileField(upload_to='featured', db_column='FEATURED_ITEM_IMG_URL', blank=True)
alt_title = models.CharField(max_length=768, db_column='FEATURED_ITEM_ALT_TITLE', blank=True)
modified = models.DateTimeField(db_column='FEATURED_ITEM_MODIFIED', auto_now=True)
folio = models.ForeignKey(Folio, db_column='FEATURED_ITEM_FOLIO_ID', blank=False)
app = models.ForeignKey(Application, null=True, db_column='FEATURED_ITEM_APP_ID', blank=True)
name = models.CharField(max_length=192, db_column='FEATURED_ITEM_NAME', blank=True)
featureditemset = models.ForeignKey(Featureditemset, null=True, db_column='FEATUREDITEM_FEATUREDITEMSET_ID', blank=True)
def __unicode__(self):
return self.name
class Meta:
db_table = u'featureditem'
Output to console:
Formset: <input type="hidden" name="item-TOTAL_FORMS" value="1" id="id_item-TOTAL_FORMS" /><input type="hidden" name="item-INITIAL_FORMS" value="1" id="id_item-INITIAL_FORMS" /><input type="hidden" name="item-MAX_NUM_FORMS" id="id_item-MAX_NUM_FORMS" />
<tr><th><label for="id_item-0-id">Id:</label></th><td><input type="text" name="item-0-id" value="5" id="id_item-0-id" /></td></tr>
<tr><th><label for="id_item-0-img">Featured Image:</label></th><td><input type="file" name="item-0-img" id="id_item-0-img" /></td></tr>
<tr><th><label for="id_item-0-name">Name:</label></th><td><input id="id_item-0-name" type="text" name="item-0-name" value="item1" maxlength="192" /></td></tr>
<tr><th><label for="id_item-0-folio">Folio:</label></th><td><select name="item-0-folio" id="id_item-0-folio">
<option value="1" selected="selected">0000054220110900000025</option>
<option value="2">com.maned.kwmultitest</option>
</select></td></tr>
<tr><th><label for="id_item-0-DELETE">Delete:</label></th><td><input type="checkbox" name="item-0-DELETE" id="id_item-0-DELETE" /><input type="hidden" name="item-0-featureditemset" id="id_item-0-featureditemset" /></td></tr>
cleaned_data: [{'id': 5}]

I figured it out. This line in my form constructor was (only sometimes?) throwing an IOError and screwing up my formset form validation/cleaning:
self.fields['img'] = forms.ImageField(label='Featured Image') # gets rid of 'clear' checkbox
When I posted my code, I took out some of the (what I thought was superfluous) form code, but what I took out turned out to be the source of the problem.

Related

Django forms - Allow a user to create a group for other users

I am trying to let a user create a "club" (basically a group) where the user later on can add users to.
Currently it does not create a field in the database somehow.
Any suggestions would be appreciated since I am fairly new to forms.
Model
class Club(models.Model):
owner = models.CharField(max_length=30)
topic = models.CharField(max_length=30)
start = models.DateTimeField(verbose_name='start date', auto_now_add=False)
end = models.DateTimeField(verbose_name='end date', auto_now_add=False)
account = models.ManyToManyField(Account)
Views
#login_required
def add_club(request):
if request.method == "POST":
form = AddClubForm(request.POST, instance=request.user)
print(form)
if form.is_valid():
form.save()
return HttpResponseRedirect(request.path_info)
else:
form = AddClubForm(instance=request.user)
return render(request, 'page/club.html', {
"form": form,
})
Form
class AddClubForm(forms.Model):
owner = forms.CharField(required=True)
topic = forms.CharField(required=False)
start = forms.DateField(required=False)
end = forms.DateField(required=False)
class Meta:
model = Club
fields = (
'owner',
'topic',
'start',
'end',
)
Template
<form method="POST" action="">
{% csrf_token %}
<div class="col-md-6">
<label class="labels">Create a club</label>
{{ form.owner }}
<input class="btn" type="submit" value="Add club">
</div>
</form>
Since this answered your problem, I am posting the solution here:
You need to add blank=True, null=True to your fields in your model, otherwise it expects them when saving the form.
class Club(models.Model):
owner = models.CharField(max_length=30)
topic = models.CharField(max_length=30, blank=True, null=True)
start = models.DateTimeField(verbose_name='start date', auto_now_add=False, blank=True, null=True)
end = models.DateTimeField(verbose_name='end date', auto_now_add=False, blank=True, null=True)
account = models.ManyToManyField(Account, blank=True)
Concerning the instance=request.user, I believe you misunderstanding the use of instance in a ModelForm.
If the request method is GET, the instance is used to populate a ModelForm with data from an existing Club object, and then pass it to your template to display the information.
If the request method is POST (or PUT), instance represent the existing Club object you want to update with data received from the form.
You usually need to use the instance arg in a DetailView (either to update or retrieve one specific Club), never when creating an object.
That's why you need to remove the instance arg in your views.py:
form = AddClubForm(request.POST)

Is it possible to 'fill' a form like this?

I want to test a form. It is working, but the test doesn't.
One field of this form is popolated by a javascript function. I can use selenium to do so, but I don't want because it's giving problems and also I want isolate the test.
So I'm calling the form in my test, then I'm creating the choices (this is what javascript should do), then I'm setting the fields values.
My models.py:
class Name(models.Model):
name = models.CharField(_('nome'), max_length=50, default='')
namelanguage = models.ForeignKey(
NameLanguage, related_name='%(app_label)s_%(class)s_language',
verbose_name=_('linguaggio'), on_delete=models.PROTECT)
nametype = models.ForeignKey(
NameType, related_name='%(app_label)s_%(class)s_tipo',
verbose_name=_('tipo'), on_delete=models.PROTECT)
gender = models.ForeignKey(
Gender, related_name='%(app_label)s_%(class)s_gender',
verbose_name=_('sesso'), on_delete=models.PROTECT,
blank=True, null=True)
usato = models.PositiveSmallIntegerField(_('usato'), default=0)
approved = models.BooleanField(null=True, blank=True, default=False)
def save(self, *args, **kwargs):
self.name = format_for_save_name(self.name)
to_save = check_gender_name(self)
if not to_save:
return
else:
super(Name, self).save(*args, **kwargs)
def format_for_save_name(name):
myname = name.lower().strip()
if myname[0] not in "abcdefghijklmnopqrstuvwxyz#":
myname = '#' + myname
return myname
My form.py:
class NameForm(forms.ModelForm):
class Meta:
model = Name
fields = ['namelanguage', 'nametype', 'gender', 'name', 'usato',
'approved']
widgets = {
'gender': forms.RadioSelect(),
'usato': forms.HiddenInput(),
'approved': forms.HiddenInput(),
}
My test_form.py:
def test_form_validation(self):
maschio = Gender.objects.create(name_en='Male', name_it='Maschio')
nome = NameType.objects.create(name_en='Name', name_it='Nome')
romani = NameLanguage.objects.create(
name_en='Romans', name_it='Romani')
romani.sintassi.add(nome)
form = NameForm()
form.fields['nametype'].disabled = False
form.fields['nametype'].choices = [(nome.id, nome)]
form.fields['nametype'].initial = nome.id
form.fields['gender'].initial = maschio.id
form.fields['name'].initial = 'Bill'
form.fields['namelanguage'].initial = romani.id
# form.fields['usato'].initial = 0
# form.fields['approved'].initial = False
print('1', form)
# self.assertTrue(form.is_valid())
form.save()
print('1', form) gives a form without errors but form.is_valid is False and (when is commented out) form.save() gives an error when the model try to save the name field:
if myname[0] not in "abcdefghijklmnopqrstuvwxyz#":
IndexError: string index out of range
That is because the name is an empty string and yet my print('1', form) gives all the fields with the right options selected and specifically the name field isn't empty but has value="Bill":
<td><input type="text" name="name" value="Bill" maxlength="50" autofocus="" required id="id_name">
Edit. I tried to avoid that check and the problem is the same for the other fields: they looks ok on the print('1', form) but they don't arrive to the form.save(), for example in my print('1', form) I have:
<tr><th><label for="id_namelanguage">Linguaggio:</label></th><td><select name="namelanguage" required id="id_namelanguage">
<option value="">---------</option>
<option value="1" selected>Romani</option>
so it looks I have selected an option but then I receive this error:
django.db.utils.IntegrityError: NOT NULL constraint failed: lists_name.namelanguage_id
I don't know why and how but this code is working:
def test_form_validation(self):
maschio = Gender.objects.create(name_en='Male', name_it='Maschio')
nome = NameType.objects.create(name_en='Name', name_it='Nome')
romani = NameLanguage.objects.create(
name_en='Romans', name_it='Romani')
romani.syntax.add(nome)
form = NameForm({'nametype': nome.id, 'gender': maschio.id,
'name': 'Remo', 'namelanguage': romani.id})
form.fields['nametype'].initial = nome.id
form.save()
self.assertEqual(Name.objects.all().count(), 1)
my_name = Name.objects.first()
self.assertEqual(my_name.name, 'remo')
self.assertEqual(my_name.nametype, nome)
self.assertEqual(my_name.gender, maschio)
self.assertEqual(my_name.namelanguage, romani)
Any comment will be appreciated

Django choice field default value and readonly

I try to set my field in form readonly and put any default value.
This is a part of my form:
category = forms.CharField(widget=forms.TextInput(attrs={'readonly':
'readonly'}),
initial=Category.objects.get(name='Zdrowie i uroda'))
class Meta:
model = Site
fields = ('url', 'name', 'description', 'keywords', 'group', 'category',
'subcategory', 'category1', 'subcategory1')
I get an error: Cannot assign "'Zdrowie i uroda'": "Site.category" must be a "Category" instance.
This is my site model:
class Site(models.Model):
category = models.ForeignKey('Category')
subcategory = ChainedForeignKey(
'SubCategory',
chained_field='category',
chained_model_field='category',
show_all=False,
auto_choose=True)
name = models.CharField(max_length=70)
description = models.TextField()
# importuje zmienione TextFields widgets.py
keywords = MyTextField()
date = models.DateTimeField(default=datetime.now, editable=False)
url = models.URLField()
is_active = models.BooleanField(default=False)
category1 = models.ForeignKey('Category', related_name='category', blank=True, null=True, default=None)
subcategory1 = ChainedForeignKey(
'SubCategory',
chained_field='category1',
chained_model_field='category',
related_name='subcategory',
show_all=False,
auto_choose=True, blank=True, null=True, default=None)
group = models.CharField(max_length=10, choices=(('podstawowy', 'podstawowy'),
('premium', 'premium')), default='podstawowy')
def get_absolute_url(self):
return reverse('site', args=[str(self.category.slug),
str(self.subcategory.slug), str(self.id)])
def get_thumb(self):
host = urlparse(self.url).hostname
if host.startswith('www.'):
host = host[4:]
thumb = 'http://free4.pagepeeker.com/v2/thumbs.php?size=s&url=' + host
return thumb
class Meta:
verbose_name_plural = "Strony"
def __str__(self):
return self.name
I can't deal with it. Any clues?
Finally I excluded category, subcategory fields from my model.form and added it to html source manually:
{{ form_extended|bootstrap }}
<label for="example-text-input" class="col-2 col-form-label">Category</label>
<input class="form-control form-control-lg" type="text" name="category" disabled required value="{{ form_extended.initial.category.name }} " />
<label for="example-text-input" class="col-2 col-form-label">Subcategory</label>
<input class="form-control form-control-lg" type="text" name="subcategory" disabled required value="{{ form_extended.initial.subcategory.name }} " />
I don't think it is a right way but I can't put some data to form initial values. Should I create my form manually from scratch? My way isn't good because I use jQuery to extend form. Now category, subcategory fields are las but they shouldn't (when user choose "premium" group from choice fields, after subcategory field there should appear "category1", "subcategory1" fields...
As always - I am sorry for my terrible English. Every post is like exam to me.
Options without further delving into Django forms:
Change Model Field
This might not be an option, but you could try setting:
class Site(models.Model):
category = models.ForeignKey('Category', editable=False)
Remove the explicit declaration of the category form field from your form and simply set the initial category when initializing the form or override __init__ to set it.
Change Form Field
Django would normally render a ForeignKeyField as drop down. Do you explicitly want a text input instead? In that case you have to handle data validation and the data mapping yourself.
A way of working around that would be to remove the explicit declaration of the form field and simply handle this field separatly in your HTML template:
<input type="text" name="category" value="{{ form.initial.category.name }}" />
"Do you explicitly want a text input instead?" - No.
In that case, you want a ModelChoiceField instead of a CharField.
category = forms.ModelChoiceField(
queryset=Category.objects,
empty_label=None,
disabled=True, # Django 1.9+
initial=Category.objects.get(name='Zdrowie i uroda'))

How to pass parameter from template to a view django

I want to pass a dropdown variable from the template to a function when i click submit button
#models.py :-
class donnee(models.Model):
name = models.CharField(_('name'), max_length= 150)
def __unicode__(self):
return self.name
class Meta:
verbose_name = _('donnee')
verbose_name_plural = _('donnees filtrage')
ordering = ['name']
class Property(models.Model):
name = models.CharField(_('name'), max_length=50)
description = models.TextField(_('description'), blank=True)
def __unicode__(self):
return self.name
class Meta:
verbose_name = _('property')
verbose_name_plural = _('properties')
ordering = ['name']
class Physic2(models.Model):
name = models.ForeignKey(Property, verbose_name=_('name'), null=True, blank=True)
lapropriete = models.ForeignKey(donnee, verbose_name=_('lapropriete'), blank=True)
#lapropriete = models.CharField(_('property'), max_length=100)
description = models.TextField(_('description'), blank=True)
def __unicode__(self):
return self.lapropriete
class Meta:
verbose_name = _('physic2')
verbose_name_plural = _('physics2')
ordering = ['name']
#forms.py:-
class Physic2Form(forms.ModelForm):
class Meta:
model = Physic2
#views.py:-
def get_materials_one(request, category_slug=None):
if category_slug is None:
lafamille= 'general'
propriete= Physic2Form()
return render_to_response('material/critere1.html',
{'unefamille': lafamille,
'propriete': propriete},
context_instance=RequestContext(request))
#template:-
<form id= "testjson" action="{% url chercher_filtre1 %}" method= "get" onsubmit = "">
{{ propriete.lapropriete }}
<td><input type="submit" style="background-color:#D7D8D4;" value="Search" id= "chercher"/></td>
</div>
</form>
#function:-
valT1 = request.GET['lapropriete']
this don't work
when i click on a field in the dropdown list
valT1 = 1 or 2... it's just the id of the field
i have the fields
Vickers hardness (GPa)
Shear Modulus (GPa)
Young Modulus (GPa)
what to put for request.GET[ ??]
You can write like this and try.
if request.method == 'GET':
form = Physic2Form(request, data=request.GET)
if form.is_valid():
data = form.cleaned_data
valT1 = data['lapropriete']
my function
def search_filter1(request):
try:
val_min1 = float(request.GET['Vmin1'])
val_max1 = float(request.GET['Vmax1'])
T_min1 = float(request.GET['Tmin1'])
T_max1 = float(request.GET['Tmax1'])
if request.method == 'GET':
form = Physic2Form(request.GET)
if form.is_valid():
valT1 = form['lapropriete']
print 'val_min1:',val_min1
print 'val_max1:',val_max1
print 'Tmin1:', T_min1
print 'Tmax1:', T_max1
print 'valT1:',valT1
If i select the property 'Shear Modulus (GPa)' when i click on submit button,
i get this :
val_min1: 44.0
val_max1: 99.0
Tmin1: 44.0
Tmax1: 99.0
valT1: <select name="lapropriete" id="id_lapropriete">
<option value="">---------</option>
<option value="2">Elasticity Modulus (GPa)</option>
<option value="4" selected="selected">Shear Modulus (GPa)</option>
<option value="1">Vickers Hardness (GPa)</option>
<option value="3">Young Modulus (GPa)</option>
</select>
why that ?

Identifying & modifying single field in Django Model

I'm using Django to build a small system to control the lending and borrowing of some stuff our Students Café lend to students.
I'm having trouble identifying an object after a form submit, I want to mark the object as 'unaivalable' (disponible means available, so I want to set it to False) so next time someone comes to ask for THAT object, it will not show up in the 'lending' form.
All I need is a hint on how to achieve it, I've been looking through Django docs, and this site, with no success. Thanks in advance for the tips!
models.py
class Mate(models.Model):
color = models.CharField(max_length=2,
choices=COLOR_CHOICES, default=u'RO')
disponible = models.BooleanField(default=True)
def __unicode__(self):
return self.color
class Prestamo(models.Model):
cliente = models.ForeignKey(Usuario, null=False, blank=False)
mate = models.ForeignKey(Mate, null=False, blank=False)
termo = models.ForeignKey(Termo, null=False, blank=False)
bombilla = models.ForeignKey(Bombilla, null=False, blank=False)
fecha = models.DateTimeField(null=False, blank=False)
devuelto = models.BooleanField(default=False)
fecha_devolucion = models.DateTimeField(null=True, blank=True)
def __unicode__(self):
return str(self.pk)
views.py
#login_required
# Add_prestamo means 'Add lending' this basically deals with prestamo model, but i want to alter 'mate' objects here too.
def add_prestamo(request):
if request.method == 'POST':
form = PrestamoForm(request.POST,
auto_id=False, error_class=DivErrorList)
if form.is_valid():
prestamo = form.save(commit=False)
if request.POST.get('usuarios'):
miuser = request.POST.get('usuarios', '')
else:
miuser = ''
prestamo.cliente = Usuario.objects.get(nombre__exact=miuser)
# I KINDA NEED SOMETHING RIGHT HERE
prestamo.fecha = timezone.now()
prestamo.devuelto = False
prestamo.save()
return HttpResponseRedirect(reverse('list-prestamos'))
else:
form = PrestamoForm()
return TemplateResponse(request,
'gester/add_prestamo.html', {'form': form, })
add_prestamo.html
<form action="" method="post">
{% csrf_token %}
<table>
<tr>
<td>
<div class="ui-widget">
<label for="usuarios">Usuario: </label></td><td>
<input id="usuarios" name="usuarios">
</div>
</td>
</tr>
{{ form.as_table }}
</table>
<input class="btn" type="submit" value="Crear" />
</form>
In the template I show the form with a {{ form.as_table }} it display a select, but many of them (mates) have the same color, so when I get through POST in my view, how do I identify the exact object to alter the 'disponible' field value?
I really don't understand your codes but because you mention disponible, I hope this is what you mean.
prestamo.fecha = timezone.now()
prestamo.devuelto = False
//Because Prestamo model has a foreignkey for Mate model.
//The Mate model contains the disponible field which you want to access
// (to set it to False or unavailable)?
//This is how to access and update it.
prestamo.mate.disponible = False
prestamo.mate.save()
prestamo.save()