Django ModelForm ValueError - django

I have a Django Model
class Category(MPTTModel):
name = models.CharField(max_length=50, unique=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
def __unicode__(self):
return self.name
class MPTTMeta:
order_insertion_by = ['name']
and ModelForm
class UploadForm(ModelForm):
file = forms.FileField()
category = mpttform.TreeNodeMultipleChoiceField(queryset=Category.objects.filter(lft=F('rght')-1))
class Meta:
model = UploadedFile
However, I'm having problem with this category field in UploadForm which is supposed to be Category instance (as definied in Model), but my queryset return list of Category objects which I use in template to show all leaf categories.If I select any category on the form and submit it, I get this error(in case I select cat5) 'Cannot assign [Category: cat5]: "UploadedFile.category" must be a "Category" instance.'
So I understand why this error is happening, but I'd like to use ModelForm because of save() method, but don't see how I can fix this.Any suggestions?

Django is telling you that you must initiate a category instance in order to iterate over the categories. So the category instance takes params from the url, url params with regex in your urls.py . So you need to capture the param and make it the category instance in the view.
*See class based generic views for which views automatically give you the params context variable.

I think
category = mpttform.TreeNodeMultipleChoiceField(queryset=Category.objects.filter(lft=F('rght')-1))
Works for a m2m relation and I guess category is a ForeignKey in the model Uploaded File. If so, you should use
category = mpttform.TreeNodeChoiceField(queryset=Category.objects.filter(lft=F('rght')-1))

Related

Use an app's model's object fields as form ChoiceField in another app

I have two apps,menu and table
This is a model in app menu
class Item(models.Model):
name = models.CharField(verbose_name="Item Name", max_length=200, db_index=True)
And in app table, I have a form.py as following:
from django import forms
class TableForm(forms.Form):
item = forms.ChoiceField(choices= xxx)
I would like to use the list of all object names in the model Item as a choicelist in the form. The objects of Item could be created both by admin and elsewhere in my project during runtime and updated in the database. Could someone advise? Thanks.
It is better to use a ModelChoiceField [Django-doc]. If you override the __str__(…) method, the method that tells how the textual representation of an object looks like, we can work with a
class Item(models.Model):
name = models.CharField(
verbose_name='Item Name',
max_length=200,
db_index=True
)
def __str__(self):
return self.name
Then the form looks like:
from django import forms
from menu.models import Item
class TableForm(forms.Form):
item = forms.ModelChoiceField(queryset=Item.objects.all())
It might be a good idea to make the name of an Item unique, such that no two Items are given the same name, this could result in confusion, since if two items are named Apple, then the question is "What is the right apple?":
class Item(models.Model):
name = models.CharField(
verbose_name='Item Name',
max_length=200,
db_index=True,
unique=True # ← mark the name as unique
)
# …

Get List of Categories on the basis of foreign model in django

I've two models as below.
class Category(models.Model):
title = models.CharField(max_length=55)
class Meta:
verbose_name = 'Food Category'
verbose_name_plural = 'Food Categories'
def __str__(self):
return self.title
class FoodItem(TimeStampWithCreator):
CATEGORY_CHOICES = (
('takeway', 'Takeaway'),
('dine_in', 'Dine In'),
('function', 'Function'),
)
type_menu_select = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='takeway')
category = models.ForeignKey(FoodCategory, on_delete=models.CASCADE)
i want to filter all the categories containing takeaway, I've no idea how to achieve this
You've included category choices in your FoodItem model but the model also has a ForeignKey to a Category model, this isn't needed if the only category objects you have are those three choices (the category field must refer to one of those anyway as it's the ForeignKey). To filter the items by category you would need to use a queryset filter.
https://docs.djangoproject.com/en/3.0/topics/db/queries/#retrieving-specific-objects-with-filters
FoodItem.objects.filter(category=YourCategory)
This is usually the kind of thing I'd like to test in the shell as I don't do it very often. If what you want is actually all Category with a FoodItem that have type_menu_select set to 'takeway' then the following should work (but I haven't tested it):
Category.objects.filter(fooditem__type_menu_select='takeway')
This is using the "reverse" relationship on the ForeignKey and there's more information in the Django docs (search for 'reverse').

how to create django form with multiplechoicefield and add those value to database

what I want to achieve is user will submit 3 inputs in the form 1) name 2) dropdown to select technician, 3) multiselect dropdown to select multiple products. Once the user submit the details
it will generate one lead in database with value like name,foreignkey of selected technician and id of selected products in different table. I don't know how to achieve this below I have mentioned my approch to achieve what I want. Please let me know if the models need any changes and how I can write a view for the same.
models.py
class product(models.Model):
name = models.CharField(max_length=20)
class technician(models.Model):
name = models.CharField(max_length=20)
class lead(models.Model):
name = models.CharField(max_length=20)
technician = models.ForeignKey(technician,on_delete=models.SET_NULL,null=True) #only single selection
products = models.ManyToManyField(product) #user can select multiple product in dropdown
form.py
class leadForm(form.ModelForm):
products = forms.MultipleChoiceField(queryset=Product.objects.all())
technician = forms.CharField(max_length=30,choices=[(i.id,i.name) for i in Technician.objects.all().values('id','name')
class Meta:
model = lead
fields = ('name','technician')
You should use a ModelMultipleChoiceField [Django-doc] here. The But in fact you do not need to implement the models yourself. You can simply let the Django logic do the work for you.
In order to give a textual representation at the HTML end, you can override the __str__ functions of the models:
class Product(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Technician(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Lead(models.Model):
name = models.CharField(max_length=20)
technician = models.ForeignKey(Technician, on_delete=models.SET_NULL, null=True)
products = models.ManyToManyField(Product)
Then we can simply define our form with:
class LeadForm(form.ModelForm):
class Meta:
model = Lead
fields = '__all__'
Note: usually classes are written in PamelCase and thus start with an Uppercase.
You can here use a class-based CreateView [Django-doc] for example:
from django.views.generic.edit import CreateView
from app.models import Lead
from app.forms import LeafForm
class LeadCreateView(CreateView):
model = Lead
form_class = LeadForm
template_name = 'create_lead.html'

HyperlinkedIdentityField and null values in django-rest-framework

I've got two models (code is simplified):
class Document(models.Model):
data = JSONField()
class Item(models.Model):
name = models.CharField(max_length=10)
doc = models.OneToOneField(
Document,
blank=True, null=True,
on_delete=models.SET_NULL
)
I would like to make a hyperlink to the Documents details page in the serializer for Item using the HyperlinkedIdentityField.
So I have the URL defined:
url(r'^doc/(?P<version>[v1|v2]+)/(?P<pk>[0-9]+)/$',
doc_details,
name = 'api_document_details'
),
And I made the following serializer:
class ItemSerializer(serializers.ModelSerializer):
doc = serializers.HyperlinkedIdentityField(
view_name = 'api_document_details',
lookup_field = 'doc_id',
lookup_url_kwarg = 'pk'
)
class Meta:
model = Item
fields = ('name', 'doc')
The problem here is that the doc field in Item is optional. I got an error as there is no URL to be found if the doc_id = None.
Could not resolve URL for hyperlinked relationship using view name "api_document_details". You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
I would like HyperlinkedIdentityField to return None (null) if the document is not defined in the record. Does anybody know how to do this?
I have tried using the SerializerMethodField() method in which I use the reverse function and return None if I get an exception, but this has two disadvantages:
I cannot see which version is requested, so I default to v1
It's a relative urlthat is generated, do the domain is not included in the returned value.

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)