Update several objects with formset - django

I'm 1st year on CS so forgive my noobness if i am totally unclear.
I have several objects from my "Products" Model. Now I would like to update the same field on all of the objects, the 'quantity' field with different values. But instead of clicking in and out of each product as with updateview, i would like to list all products and set the value for each and change them at the simultaneously. As far as i can see "FormSet" should do the trick?
My category model looks like this (to assign for a product)
class Category(models.Model):
category = models.CharField(max_length=30)
def __str__(self):
return self.category
My Product model looks like this:
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
category = models.ForeignKey(Category)
price = models.DecimalField(max_digits=5, decimal_places=2)
stock = models.PositiveIntegerField()
def __str__(self):
return self.title
My update view for updating single product looks like this:
class UpdateProductView(UpdateView):
model = Product
form_class = ProductForm
template_name = "product_form.html"
success_url = '/products'
class CreateCategoryView(FormView):
template_name = "category_form.html"
form_class = CategoryForm
I read the documentation on formset, but i gotta admit i didn't feel much smarter about how to actually use it afterwards.. can anyone give a hand?

Didn't find quantity field on your Product model, but as I can see, you want to use ModelFormSet.
# Generate your formset class with `modelformset_factory`
ProductFormSetClass = modelformset_factory(Product, fields=('quantity',))
# Now you can create your formset, bind data, etc
formset = ProductFormSetClass(request.POST)
formset.save()
More details by link:
https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#model-formsets
Also, don't forget to inspect all modelformset_factory arguments.

Related

Cannot assign "id": "Product.category" must be a "CategoryProduct" instance

i'm working on a django project and i got this error (Cannot assign "'11'": "Product.category" must be a "CategoryProduct" instance.) anyone here can help me please.
Model:
class Product(models.Model):
name = models.CharField("Nombre", max_length=150)
category = models.ForeignKey(CategoryProduct, on_delete=models.SET_NULL, null=True, related_name='category')
def __str__(self):
return self.name
View:
class ProductCreateView(CreateView):
model = Product
form_class = ProductForm
success_url = '/adminpanel/products/'
def post(self, request, *args, **kwargs):
form = self.get_form()
category = CategoryProduct.objects.get(id=request.POST['category'])
if form.is_valid():
product = form.save(commit=False)
product.category = category
product.save()
Form:
class ProductForm(forms.ModelForm):
name = forms.CharField(max_length=150, label="Nombre")
category = forms.ChoiceField(choices=[(obj.id, obj.name) for obj in CategoryProduct.objects.all()], label="Categoría")
class Meta:
model = Product
fields = ['name', 'category']
You can let Django's ModelForm do its work, this will create a ModelChoiceField [Django-doc], which is where the system gets stuck: it tries to assign the primary key to category, but that should be a ProductCategory object, so you can let Django handle this with:
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ['name', 'category']
If you want to specify a different label, you can use the verbose_name=… [Django-doc] from the model field, or specify this in the labels options [Django-doc] of the Meta of the ProductForm. So you can specify Categoria with:
class Product(models.Model):
name = models.CharField('Nombre', max_length=150)
category = models.ForeignKey(
CategoryProduct,
on_delete=models.SET_NULL,
null=True,
related_name='products',
verbose_name='Categoria'
)
def __str__(self):
return self.name
then the CreateView can just use its boilerplate logic:
class ProductCreateView(CreateView):
model = Product
form_class = ProductForm
success_url = '/adminpanel/products/'
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the Category model to the Product
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the category relation to products.

django auto complete light says field is required on ForeignKey

I have two models:
class ProductCategory(models.Model):
'''
Product Category determines to which category the product falls into.
'''
category_name = models.CharField(max_length=254)
def __unicode__(self):
return u"%s" %(self.category_name)
class Meta:
verbose_name_plural = "Product Categories"
class Product(models.Model):
'''
Product Information
'''
name = models.CharField(max_length=250)
category = models.ForeignKey(ProductCategory)
def __unicode__(self):
return u"%s" %(self.product_name)
I want to apply autocomplete on category field of product model. Thus,
class ProductCategoryAutoComplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['category_name']
model = Product
choices = ProductCategory.objects.all()
autocomplete_light.register(Product, ProductCategoryAutoComplete)
I have included the template too. Everything works fine. Except when I choose the category and submit the form html field is required is popping up in the bottom. What is wrong?
Edit: Form
class ProductCreateForm(autocomplete_light.ModelForm):
category = forms.ModelChoiceField(queryset=ProductCategory.objects, widget=autocomplete_light.ChoiceWidget('ProductCategoryAutoComplete'))
class Meta:
model = Product
Oops !
Product.category is an FK to model Category, but the Autocomplete you are passing (ProductCategoryAutoComplete) is registered for Product model ! Field that should allow selecting a Category should use an autocomplete for Category, not one for Product ;)
Better usage of autocomplete_light.ModelForm
Since you're using autocomplete_light.ModelForm, you don't have to specify the field. Just register an autocomplete for Category:
autocomplete_light.register(Category, search_fields=['category_name'])
And let autocomplete_light.ModelForm do the field definition:
class ProductCreateForm(autocomplete_light.ModelForm):
class Meta:
model = Product
Yes, that's all you need ;)
search_fields is wrong, product does`n have field category_name, if I understand your idea it have to be:
class ProductCategoryAutoComplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['category__category_name']
model = Product
choices = ProductCategory.objects.all()
autocomplete_light.register(Product, ProductCategoryAutoComplete)

Using prefetch_related for reducing queries overhead

I have two models.
class Category(models.Model):
title = models.CharField(max_length=60, unique=True)
def __unicode__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=60, unique=True)
category = models.ForeignKey(Category)
#code
views.py
class PostList(ListView):
model = Post
def get_queryset(self):
queryset = Post.objects.all().\
select_related('category')
But for my main page i need further all categories for the navbar. What the best approach to add categories to get_queryset()?
I tried to use prefetch_related.
post = Post.objects.all().prefetch_related('category')
but i don't understand how to fetch all categories.
Is it correct solution?
class PostList(ListView):
def get_queryset(self):
p = Post.objects.all().\
select_related('category') #like tag for each post
p.categories = Category.objects.all() #all categories for navbar
return p
Or Django has own methods for this task?
I don't understand what this has to do with either select_related or prefetch_related. Neither of those will help in getting categories that are not related to your posts: as their names imply, they are to do with getting related items, not unrelated ones.
If you need all the categories, you should simply do Category.objects.all().

Django framework: ListView with CreateView

I am new in django programming, and I have simple question.
I have model
class Dhcp(models.Model):
class Meta:
ordering = ('gateway',)
verbose_name = _(u'DHCP Configuration')
verbose_name_plural = _(u'DHCP Configurations')
gateway = models.IPAddressField(_(u'Gateway'), null=True)
dns_primary = models.IPAddressField(_(u'DNS Primary'), null=True)
dns_second = models.IPAddressField(_(u'DNS Second'), blank=True)
leases_time = models.IntegerField()
nat = models.IPAddressField(_(u'NAT'), blank=True)
max_time = models.IntegerField()
def __unicode__(self):
return self.gateway
and my question is :
And I want to list all my dhcp objects which are in database, and add more object. But I want do that on same template.
For example: If I have 3 objects in database, show that and show form for add more obj. When I add one more object from form, I want to show the added object.
Thanks!
Dusan Ristic
Have a look into Class Based Views, more specifically the ListView and CreateView. This question comes up quite often, so you should find the answer on here somewhere. Check this post out.
What you're looking for is something like this:
class DHCPView(forms.CreateView):
model = Dhcp
template_name = "list_and_create_dhcp.html"
def get_context_data(self, **kwargs):
kwargs["object_list"] = Dhcp.objects.all()
return super(DHCPView, self).get_context_data(**kwargs)

How to save a ManyToMany relationship from a ModelForm in Django

I'm not sure to save my ManyToMany relationship. I found my exact problem in this thread: Django embedded ManyToMany form, except instead of Sales and Products models, I have models that make up a movie.
I tried the solution, but I receive a syntax error. I don't understand how Django should link the EquipmentModel, LightModel, and ActorModel to the ManyToMany relationship in MovieModel. So far (before trying the other thread's solution), the CharFields that are displayed on the form for LightModel, EquipmentModel, and ActorModel are not linked to the ManyToManyField in MovieModel. So when I save the forms and try to access a particular Movie's actors, all I see is a blank list. The solution from the other thread seems to make sense since it tries to link the models to the ManyToMany relationship in MovieModel, but I don't understand how Django knows which MovieModel to add to (how does it get the correct movieID?).
On a side note, is there a way to check for duplicate movies when the user presses the 'Submit' button on the form? I want to avoid creating duplicates.
views.py:
def add_movie(request, movieID=""):
if request.method == "POST":
form = MovieModelForm(request.POST)
eform = EquipmentModelForm(request.POST)
lform = LightModelForm(request.POST)
aform = ActorModelForm(request.POST)
print 'checking form'
print request.POST.items()
if form.is_valid() and eform.is_valid() and lform.is_valid() and aform.is_valid():
print 'form is valid'
movie_to_add = form.save()
e = eform.save()
l = lform.save()
a = aform.save()
movie_to_add.actors.add(a)
movie_to_add.lights.add(l)
movie_to_add.equipments.add(e)
# return HttpResponseRedirect('/data')
else:
# code for create forms ....
return render_to_response('add_movie.html', {'form':form, 'eform':eform,'lform':lform, 'aform':aform,}, context_instance=RequestContext(request))
Other code that may help:
forms.py
class LightModelForm(forms.ModelForm):
class Meta:
model = LightModel
class ActorModelForm(forms.ModelForm):
class Meta:
model = ActorModel
class EquipmentModelForm(forms.ModelForm):
class Meta:
model = EquipmentModel
class MovieModelForm(forms.ModelForm):
class Meta:
model = MovieModel
fields = ("title", "rank")
models.py
class EquipmentModel(models.Model):
equip = models.CharField(max_length=20)
class ActorModel(models.Model):
actor = models.CharField(max_length=20)
class LightModel(models.Model):
light = models.CharField(max_length=20)
class MovieModel(models.Model):
rank = models.DecimalField(max_digits=5000, decimal_places=3)
title = models.CharField(max_length=20)
equipments = models.ManyToManyField(EquipmentModel, blank=True, null=True)
actors = models.ManyToManyField(ActorModel, blank=True, null=True)
lights = models.ManyToManyField(LightModel, blank=True, null=True)
def __str__(self):
return self.title
Edit: removed unnecessary init and fields thanks to DTing
Edit2: Fixed!
There is a whole lot of stuff going wrong here in addition to what spulec said.
Your models.py look okay.
class EquipmentModel(models.Model):
equip = models.CharField(max_length=20)
class ActorModel(models.Model):
actor = models.CharField(max_length=20)
class LightModel(models.Model):
light = models.CharField(max_length=20)
class MovieModel(models.Model):
rank = models.DecimalField(max_digits=5000, decimal_places=3)
title = models.CharField(max_length=20)
equipments = models.ManyToManyField(EquipmentModel, blank=True, null=True)
actors = models.ManyToManyField(ActorModel, blank=True, null=True)
lights = models.ManyToManyField(LightModel, blank=True, null=True)
def __str__(self):
return self.title
You don't need to override the __init__ method on forms if you are not changing anything on init. You also don't need to be explicit about the fields if you want to include them all.
class LightModelForm(forms.ModelForm):
class Meta:
model = LightModel
class ActorModelForm(forms.ModelForm):
class Meta:
model = ActorModel
class EquipmentModelForm(forms.ModelForm):
class Meta:
model = EquipmentModel
class MovieModelForm(forms.ModelForm):
class Meta:
model = MovieModel
fields = ("title", "rank")
your view doesn't really make sense unless for every movie you are trying to add you also want to:
add a new movie to the db using the submitted post data
create one actor object and add to db
create one light object and add to db
create one equipment object and add to db
take those three objects and add them to another movie's m2m relationships.
This other movie is some movie that you pulled from the urlconf and passed to your view, not the one you just created.
This all seems a little strange.
what i think you want to do is create all the equipment, actors and lights objects so they are in your db already, and use the default m2m formfield widget to select them when adding a movie.
so:
forms.py
class MovieModelForm(forms.ModelForm):
class Meta:
model = MovieModel
urls.py:
url(r'^add_movie/$', add_movie)
views.py:
def add_movie(request):
if request.method=='POST':
form = MovieModelForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('success')
else:
form = MovieModelForm()
context = {'form':form }
return render_to_response('some_template.html', context,context_instance=RequestContext(request))
you could combine adding actors, lights, and equipment into the same form but that's a bit much for me to write out right now.
As far as modifying your original code to add those lights, actors, and equipment to the movie you just created, you could do this:
if form.is_valid() and eform.is_valid() and lform.is_valid() and aform.is_valid():
new_movie = form.save()
e = eform.save()
l = lform.save()
a = aform.save()
new_movie.actors.add(a)
new_movie.lights.add(l)
new_movie.equipments.add(e)
Change it to:
movie_to_add = get_object_or_404(MovieModel, id=movieID)