Django - Display child elements based on parent in ModelChoiceField queryset - django

I have created models that each of them based on own parent. On forms I have used ModelChoiceField. When the user selects the element from Category choice field, on Subcategory field should be displayed only its child elements. And also after selecting the Subcategory, on ProductCategory choice field should be displayed child elements.
it should have been something like this
But I am getting error:
TypeError: Field 'id' expected a number but got <django.forms.models.ModelChoiceField object at 0x7f3d1f9f0ac0>. How can I improve it?
models.py:
class Category(models.Model):
name = models.CharField("Category Name", max_length=100, unique=True)
link = models.CharField("Category Link", max_length=250)
def __str__(self):
return self.name
class Subcategory(models.Model):
parent = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
name = models.CharField("Sub-category Name", max_length=100, unique=True)
link = models.CharField("Sub-category Link", max_length=250)
def __str__(self):
return self.name
class ProductCategory(models.Model):
parent = models.ForeignKey(Subcategory, on_delete=models.CASCADE, null=True)
name = models.CharField("Product-Category Name", max_length=100, unique=True)
link = models.CharField("Product-Category Link", max_length=250)
def __str__(self):
return self.name
class ProductSubCategory(models.Model):
parent = models.ForeignKey(ProductCategory, on_delete=models.CASCADE, null=True)
name = models.CharField("Product-Sub-Category Name", max_length=100, unique=True)
link = models.CharField("Product-Sub-Category Name", max_length=250)
def __str__(self):
return self.name
forms.py
class CategoryForm(forms.Form):
category = forms.ModelChoiceField(queryset=Category.objects.all())
subcategory = forms.ModelChoiceField(queryset=Subcategory.objects.filter(parent=category))
prodcategory = forms.ModelChoiceField(queryset=ProductCategory.objects.filter(parent=subcategory))
On views.py I am just running external script
def homepage(request):
context = {}
form = CategoryForm()
context['form'] = form
if request.GET:
temp = request.GET['prodcategory']
selected = ProductCategory.objects.get(pk=temp)
scraper = Scraper()
results = scraper.scrape(title=selected, link=selected.link)
data = ProductSubCategory.objects.filter(parent=results)
context['data'] = data
return render(request, 'index.html', context)

There is a well explained article on how to approach chained drop down lists in Django forms :
How to Implement Dependent/Chained Dropdown List with Django
or you could use an external library like django-smart-selects

Related

Query with multiple foreign keys (django)

I'm making a searchbar for a site I'm working on and I'm having trouble when I want to filter different fields from different models (related between them) Here are my models:
class Project(models.Model):
name = models.CharField(max_length=250)
objective = models.CharField(max_length=250)
description = models.TextField()
launching = models.CharField(max_length=100, null=True, blank=True)
image = models.ImageField(
upload_to='imgs/', null=True, blank=True)
image_thumbnail = models.ImageField(
upload_to='thumbnails/', null=True, blank=True)
slug = models.CharField(max_length=250)
class Meta:
db_table = 'project'
def __str__(self):
return self.name
class Institution(models.Model):
name = models.CharField(max_length=250)
project = models.ManyToManyField(Proyecto)
class Meta:
db_table = 'institution'
def __str__(self):
return self.name
And I want to be able to search by the name of the project or the institution, but my code only takes the institution's name.
def searchbar(request):
if request.method == 'GET':
search = request.GET.get('search')
post = Project.objects.all().filter(name__icontains=search, institution__name__icontains=search)
return render(request, 'searchbar.html', {'post': post, 'search': search})
How can I search for all the projects that match by its name OR the institution's name?
BTW, I'm using SQL, not sure if it's relevant, but I thought I should add that info.
You can .filter(…) [Django-doc] with Q objects [Django-doc]:
from django.db.models import Q
Project.objects.filter(Q(name__icontains=search) | Q(institution__name__icontains=search))
or you can work with the _connector parameter:
from django.db.models import Q
Project.objects.filter(
name__icontains=search,
institution__name__icontains=search,
_connector=Q.OR
)

Filter queryset of django inline formset based on attribute of through model

I have a basic restaurant inventory tracking app that allows the user to create ingredients, menus, and items on the menus. For each item on a given menu, the user can list the required ingredients for that item along with a quantity required per ingredient for that item.
Menu items have a many-to-many relationship with ingredients, and are connected via an "IngredientQuantity" through table.
Here are my models:
class Ingredient(models.Model):
GRAM = 'Grams'
OUNCE = 'Ounces'
PIECE = 'Pieces'
UNIT_CHOICES = [
('Grams', 'Grams'),
('Ounces', 'Ounces'),
('Pieces', 'Pieces')
]
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
unitType = models.CharField(max_length=200, choices=UNIT_CHOICES, verbose_name='Unit')
unitCost = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Unit Cost')
inventoryQuantity = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Quantity')
def __str__(self):
return self.name + ' (' + self.unitType + ')'
def totalCost(self):
result = self.inventoryQuantity * self.unitCost
return "{:.0f}".format(result)
class Menu(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
timeCreated = models.DateTimeField(auto_now_add=True)
timeUpdated = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.name)
class MenuItem(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
ingredients = models.ManyToManyField(Ingredient, through='IngredientQuantity')
menu = models.ForeignKey(Menu, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
def itemCost(self):
relevantIngredients = IngredientQuantity.objects.filter(menuItem=self)
cost = 0
for ingredient in relevantIngredients:
cost += (ingredient.ingredient.unitCost * ingredient.ingredientQuantity)
return cost
class IngredientQuantity(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
menuItem = models.ForeignKey(MenuItem, on_delete=models.CASCADE)
ingredientQuantity = models.IntegerField(default=0)
def __str__(self):
return str(self.ingredient)
This is a multi-user app, so when a user creates a new item on a menu and adds ingredients to it, they should only have the option of choosing ingredients they have created, not those of other users. Here is my attempt to do that in my views:
def ItemUpdate(request, pk):
item = MenuItem.objects.get(id=pk)
user = request.user
IngredientQuantityFormset = inlineformset_factory(
MenuItem, IngredientQuantity, fields=('ingredient', 'ingredientQuantity'), can_delete=True, extra=0
)
form = ItemCreateForm(instance=item)
formset = IngredientQuantityFormset(instance=item, queryset=IngredientQuantity.objects.filter(ingredient__user=user))
if request.method == 'POST':
form = ItemCreateForm(request.POST, instance=item)
formset = IngredientQuantityFormset(request.POST, instance=item, queryset=IngredientQuantity.objects.filter(ingredient__user=user))
# rest of view...
I've searched everywhere for how to implement the queryset parameter properly, but I cannot get it to work. When creating an item on a menu, the user still has the ability to choose from every ingredient in the database (including the ones created by other users). I would like the user to only be able to choose from the ingredients they themselves created.
Does anyone know how to do this properly? Thank you!
I received some guidance on Django forums and arrived at a solution which is documented below:
https://forum.djangoproject.com/t/filter-dropdown-options-in-django-inline-formset-based-on-attribute-of-through-model/13374/3

Djnago Form getting Error while edit record

I am getting Issue while edit a record based on CHatquestion ID, if option is null then i need to add a record based on same chatquestion id, if chatqustion id exist in option it will work,
i am trying to multiple way to solve this issue but still can't find solution.
Models.py # thease are all 3 models
class Problem(models.Model):
Language = models.IntegerField(choices=Language_CHOICE, default=1)
type = models.CharField(max_length=500, null=True, blank=True)
def __str__(self):
return self.type
class ChatQuestion(models.Model): # Eding record based on chatquestion id
question = RichTextField(null=True, blank=True)
problem_id = models.ForeignKey(
Problem,
models.CASCADE,
verbose_name='Problem',
)
def __str__(self):
return self.question
is_first_question = models.BooleanField(default=False)
class Option(models.Model):
option_type = models.CharField(max_length=250, null=True, blank=True)
question_id = models.ForeignKey(
ChatQuestion,
models.CASCADE,
verbose_name='Question',
null=True,
blank=True
)
problem=models.ForeignKey(
Problem,
models.CASCADE,
verbose_name='Problem',
null=True,
blank=True
)
next_question_id = models.ForeignKey(ChatQuestion, on_delete=models.CASCADE, null=True, blank=True,
related_name='next_question')
def __str__(self):
return self.option_type
forms.py
class EditQuestionForm(forms.ModelForm):
class Meta:
model = ChatQuestion
fields =('question','problem_id')
class EditOptionForm(forms.ModelForm):
class Meta:
model = Option
fields =('option_type',)
views.py
def question_edit(request,id=None):
if id is not None:
queryset = get_object_or_404(ChatQuestion,pk=id)
queryset1=get_object_or_404(Option,question_id=queryset )
else:
queryset = None
queryset1 = None
if request.method=="POST":
form = EditQuestionForm(request.POST ,instance=queryset)
form1=EditOptionForm(request.POST, instance=queryset1)
if form.is_valid() and form1.is_valid():
question=form.cleaned_data['question']
option_type=form1.cleaned_data['option_type']
if id:
queryset.question=question
queryset.save()
queryset1.option_type=option_type
queryset1.save()
messages.success(request,'Sucessful')
return redirect('/fleet/list_chatbot')
else:
print(form.errors)
messages.error(request,'Please correct following',form.errors)
elif id:
form = EditQuestionForm(instance=queryset)
form1=EditOptionForm(instance=queryset1)
if not queryset1:
form1=EditOptionForm()
else:
form = EditQuestionForm()
form1=EditOptionForm()
context={
'form':form,
'form1':form1
}
return render(request,'chatbot/question_edit.html',context=context)

django add parents to field

I have a Product model that has a ManyToMany to Category.
Category has a ForeignKey to itself named parent.
I want to add all parents of selected category to category field.
example for category:
digital appliance->None __ Mobile->digital appliance __ Samsung->Mobile and...
when choose Samsung for category of a product, I want to add Mobile and digital appliance to category
it's my models, the save method doesn't do anything
Class Product:
class Product(models.Model):
STATUS_CHOICES = (
('s', 'show'),
('h', 'hide'),
)
title = models.CharField(max_length=150)
slug = models.SlugField(max_length=170, unique=True)
category = models.ManyToManyField(Category)
thumbnail = models.ImageField(upload_to='images/products', default='images/no-image-available.png')
image_list = ImageSpecField(source='thumbnail', processors=[ResizeToFill(400, 200)], format='JPEG',options={'quality': 75})
image_detail = ImageSpecField(source='thumbnail', processors=[ResizeToFill(1000, 500)], format='JPEG',options={'quality': 100})
description = models.TextField()
inventory = models.IntegerField()
features = models.ManyToManyField(Feature)
status = models.CharField(max_length=1, choices=STATUS_CHOICES, default='s')
def __str__(self):
return self.title
class Meta:
verbose_name = "product"
verbose_name_plural = "products"
def save(self, *args, **kwargs):
for cat in self.category.all():
if cat.parent:
self.category.add(cat.parent)
return super(Product, self).save(*args, **kwargs)
objects = ProductManager()
Category and CategoryManager:
class CategoryManager(models.Manager):
def no_parent(self):
return self.filter(parent=None)
def get_parent(self, parent):
return self.filter(parent=parent)
class Category(models.Model):
parent = models.ForeignKey('self', default=None, null=True, blank=True, on_delete=models.SET_NULL,related_name='children')
title = models.CharField(max_length=40)
slug = models.SlugField()
status = models.BooleanField(default=True)
I think it makes more sense to have Foreign Key to category table rather than m2m relation. You can flatten it in the view whenever needed

Returning Related (1:M) Django Objects with many as a List

I'm new to Django and I'm building a Document Visibility/Status application. Each document, called an "Indent", has some "Attributes", and these attributes have multiple "Status".
On the dashboard of the application, I need to dynamically render the attributes as columns, and the statuses as choices in a dropdown list.
I'm thinking that I'll need to return a dictionary as follows - but if you have a better suggestion, please let me know.
{ 1 : ['status1','status2', 'status3'], 2 : ['status1','status2', 'status3'], ... }
Where the number is the attribute ID.
Here are my models:
class Indent(models.Model):
dateCreated = models.DateTimeField(auto_now=True)
indentNumber = models.CharField(max_length=75)
creator = models.ForeignKey(settings.AUTH_USER_MODEL)
def __str__(self):
return '%s' % (self.indentNumber)
class Meta:
verbose_name = 'Indents'
verbose_name_plural = "Indents"
class Files(models.Model):
fileLink = models.FileField(upload_to='indents/', null=True) #Add 'indents/userID'
indent = models.ForeignKey(Indent, on_delete=models.CASCADE)
def __str__(self):
return '%s' % (self.indent)
class Meta:
verbose_name = 'Files'
verbose_name_plural = "Files"
"""
TO-DO
REMOVE NULL CONSTRAINT
"""
class Attribute(models.Model):
name = models.CharField(max_length=75)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
def __str__(self):
return '%s' % (self.name)
class Meta:
verbose_name = 'Attributes'
verbose_name_plural = "Attributes"
"""
TO-DO
For the names of Status objects, it needs to be more descriptive and accurate
"""
class Status(models.Model):
name = models.CharField(max_length=255)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
def __str__(self):
return '%s' % (self.name)
class Meta:
verbose_name = 'Status'
verbose_name_plural = "Status"
class IndentAttribute(models.Model):
indent_id = models.ForeignKey(Indent, on_delete=models.CASCADE)
attribute_id = models.ForeignKey(Attribute, on_delete=models.CASCADE)
status_id = models.ForeignKey(Status, on_delete=models.CASCADE, null=True)
def __str__(self):
return '%s - %s' % (self.indent_id, self.attribute_id)
class Meta:
verbose_name = 'Indent_Attribute'
verbose_name_plural = "Indent_Attribute"
Here's an example of the dashboard we're building:
Dashboard Example
Take a look at Django's QuerySet values_list(). Here's a modified example from there to demonstrate how it works:
>>> Status.objects.values_list('name', flat=True)
<QuerySet ['Okay', 'Not Okay', 'Uh-oh', ...]>
You should be able to use that to help construct that dict pretty quickly.