django add parents to field - django

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

Related

Filter in get_context_data and get_query_set not working

I have a listview where I'm trying to filter out products by category. Some products have a subcategory. When a product has a subcategory I want the listview to display them by subcategory.
Problem is: The listview works perfect for items with a subcategory, but does not work for items who do not have a subcategory. Where am I taking a wrong turn here?
Models:
class Category(models.Model):
category_name = models.CharField(max_length=200)
sub_category = models.CharField(max_length=200,blank=True,null=True)
category_picture = ResizedImageField(upload_to='category/', null=True, blank=True)
category_info = models.TextField(blank=True, null=True)
category_video = models.CharField(max_length=250,blank=True, null=True)
def __str__(self):
if self.sub_category is None:
return self.category_name
else:
return f" {self.sub_category}"
class Meta:
ordering = ['category_name']
class Bottle(models.Model):
category_name = models.ForeignKey('Category', on_delete=models.SET_NULL,null=True,blank=True)
brand = models.ForeignKey('Brand', on_delete=models.CASCADE)
bottle_name = models.CharField(max_length=255)
bottle_info = models.TextField()
bottle_tasting_notes = models.TextField()
bottle_barcode = models.IntegerField()
bottle_image = ResizedImageField(upload_to='bottles/',null=True, blank=True)
bottle_shop_link = models.CharField(max_length=250, null=True, blank=True)
def __str__(self):
return f"{self.brand}, {self.bottle_name}"
class Meta:
ordering = ['bottle_name']
View:
class BottlesByCategoryView(ListView):
model = Bottle
context_object_name = 'bottles'
#Filter bij subcategory in the category model. If no subcategory exists, load by category_name
def get_queryset(self):
if Bottle.objects.filter(category_name__sub_category=self.kwargs['category']) is None:
return Bottle.objects.filter(category_name__category_name=self.kwargs['category'])
else:
return Bottle.objects.filter(category_name__sub_category=self.kwargs['category'])
def get_context_data(self, **kwargs):
context = super(BottlesByCategoryView, self).get_context_data(**kwargs)
if Bottle.objects.filter(category_name__sub_category=self.kwargs['category']) is None:
context['category_info'] = Category.objects.filter(category_name=self.kwargs['category'])
else:
context['category_info'] = Category.objects.filter(sub_category=self.kwargs['category'])
return context
URLS:
path('BottlesByCategory/<str:category>/',BottlesByCategoryView.as_view(template_name='academy/bottlesByCat_list.html'),name='bottlesByCat_list'),
Can i not use if statements in the get_context_data and get_query_set?

Django: Names are not showing in drop-down list

I need to show a list of countries for users to select from the ship's country field. But it's showing the Country object(1), Country object(2)... instead of showing names of countries
I've created classes for Ship and Country with the Ship class having a foreign key of country.
class Ship(models.Model):
# Fields
name = models.CharField(max_length=255)
slug = extension_fields.AutoSlugField(populate_from='name', blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
callsign = models.CharField(max_length=50)
last_updated = models.DateTimeField(auto_now=True, editable=False)
weight = models.DecimalField(max_digits=20, decimal_places=4)
# RelationShip Fields
shipflag = models.ForeignKey(
'manifest.Country', on_delete=models.SET_NULL,
related_name="Ships", null=True
)
class Meta:
ordering = ('-created',)
def __unicode__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('manifest_Ship_detail', args=(self.slug,))
def get_update_url(self):
return reverse('manifest_Ship_update', args=(self.slug,))
class Country(models.Model):
# Fields
name = models.CharField(max_length=255)
slug = extension_fields.AutoSlugField(populate_from='name', blank=True)
created = models.DateTimeField(auto_now_add=True, editable=False)
last_updated = models.DateTimeField(auto_now=True, editable=False)
code = models.CharField(max_length=5)
# RelationShip Fields
continent = models.ForeignKey(
'manifest.Continent',
on_delete=models.CASCADE, related_name="Countrys",
)
class Meta:
ordering = ('-created',)
def __unicode__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('manifest_Country_detail', args=(self.slug,))
def get_update_url(self):
return reverse('manifest_Country_update', args=(self.slug,))
In the 'create new ship' form at the country dropdown combo I expect to see a list of countries like United States, Mexico, Canada... but instead am seeing countries as objects like this object(1), Country object(2)...
add this method to your models. ;)
def __str__(self):
return self.name

Django - 2 Foreign Key fields in one model (limit choices)

I am trying to add simple option to my admin panel. I have category, subcategory and site models. For example:
Computers (category)
- PC (subcategory)
- Notebooks (subcategory)
Health (category)
- Diet (subcategory)
- Fitness (subcategory)
Subcategory is within category. When I am adding site in my admin panel I have list of all categories and subcategories. When I choose category (Computers) in my Subcategory field I have: PC, Notebooks, Diet, Fitness (all subcategories). I don't have any idea how can I filter this only to PC and Notebooks. Any suggestions?
class Category(models.Model):
name = models.CharField(max_length=30, unique=True,
verbose_name='Nazwa kategorii')
slug = models.SlugField()
image = models.ImageField(upload_to='category_images',
verbose_name="Image",
blank=True)
description = models.TextField(default='Description',
verbose_name="Category description")
class Meta:
verbose_name_plural = "Categories"
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
def image_thumb(self):
if self.image:
return '<img src="/media/%s" width="40" height="40" />' % (self.image)
else:
return('')
image_thumb.short_description = 'Thumb'
image_thumb.allow_tags = True
def __str__(self):
return self.name
class SubCategory(models.Model):
category = models.ForeignKey(
'Category',
related_name='subcategory',
on_delete=models.CASCADE,
blank=True,
null=True,
)
name = models.CharField(max_length=30)
class Meta:
verbose_name_plural = "Subcategories"
def __str__(self):
return self.name
class Site(models.Model):
category = models.ForeignKey('Category')
# sub = SubCategory.objects.filter(category=category)
subcategory = models.ForeignKey('SubCategory', related_name='subcategory')
name = models.CharField(max_length=30)

How to create an inline formset for a reverse foreign key relationship

I have a Property Model as follows =
class Property(models.Model):
property_type = models.CharField(max_length=255, default='Apartment')
specifications = models.CharField(max_length=255, default='Basic')
built_up_area = models.FloatField(max_length=6, null=False, default=0)
total_area = models.FloatField(null=False, default=0)
number_of_bedrooms = models.CharField(max_length=3, default=1)
number_of_bathrooms = models.CharField(max_length=3, default=1)
number_of_parking_spaces = models.CharField(max_length=2, default=0)
address_line_one = models.CharField(max_length=255, null=False)
address_line_two = models.CharField(max_length=255, null=True, default=None)
connectivity = models.CharField(max_length=255, default=None, null=True)
neighborhood_quality = models.CharField(max_length=255, default=None,
null=True)
comments = models.CharField(max_length=255, default=None, null=True)
city = models.ForeignKey('City')
state = models.ForeignKey('State')
pin_code = models.ForeignKey('PinCode')
developer = models.ForeignKey('Developer', null=True, default=None)
owner = models.ForeignKey('Owner', null=True, default=None)
created_by = models.ForeignKey('custom_user.User')
project = models.ForeignKey('Project')
def __unicode__(self):
return self.property_type
class Meta:
verbose_name_plural = 'Properties'
And a City model as follows -
class City(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(City, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
Now I want to make a single form where I can enter the Property details and while entering the city, I can enter the name of the city instead of selecting from the dropdown list.
So how do I create the inline formset using the inlineformset_factory to create the form?
==EDIT==
I've tried to use the following code to create the formset
CityFormset = inlineformset_factory(City, Property,
fields=('city',),
extra=0,
min_num=1,
can_delete=False)
You've misunderstood what an inline formset is. It's for editing the "many" side of a one-to-many relationship: that is, given a parent model of City, you could edit inline the various Properties that belong to that city.
You don't want a formset at all to simply edit the single City that a property can belong to. Instead, override the city field within your Property form to be a TextField, and either create a new City or find an existing one in the clean_city method.
class PropertyForm(forms.ModelForm):
city = forms.TextField(required=True)
class Meta:
model = Property
exclude = ('city',)
def __init__(self, *args, **kwargs):
super(PropertyForm, self).__init__(*args, **kwargs)
if self.instance and not self.data:
self.initial['city'] = self.instance.city.name
def save(self, commit=True):
city_name = self.cleaned_data['city']
city, _ = City.objects.get_or_create(name=city_name)
instance = self.save(commit=False)
instance.city = city
if commit = True:
instance.save()
return instance

Get all parent categories Django - Follow foreign key UP

I got the following models:
class Category(models.Model):
name = models.CharField(max_length=255)
parent = models.ForeignKey("self", blank=True, null=True)
class Meta:
verbose_name = _("category")
verbose_name_plural = _("categories")
def __unicode__(self):
return self.name
class Item(models.Model):
name = models.CharField(max_length=100, verbose_name=_("name"))
keywords = models.CharField(max_length=255, verbose_name=_("keywords"))
category = models.ForeignKey(Category)
class Meta:
abstract = True
verbose_name = _('item')
verbose_name_plural = _('items')
class Product(Item):
price = models.DecimalField(decimal_places=2, max_digits=8, verbose_name=_("price"))
brand = models.ForeignKey(Brand, verbose_name=_("brand"))
article_number = models.CharField(max_length=255, verbose_name=_("article_number"))
def __unicode__(self):
return self.name
class Meta:
verbose_name = _('product')
verbose_name_plural = _('products')
Let's say i have the following categories in the database:
ID NAME PARENT_ID
1 Products null
2 Phones 1
3 iPhones 2
I can get the top category by doing the following:
#This is a product with the category "iPhones"
product.category.parent.parent
But that's not nice because a product can be x numbers of categories deep.
How can I get all the related categories in an array or something?
Wanted output = [iPhones, Phones, Products]
Write a Model Property Method for the item class:
class Item(models.Model):
#property
def all_categories(self):
categories = []
current_category = self.category
while current_category is not None:
categories.append(current_category)
current_category = current_category.parent
return categories
#optional return reversed list:
#return list(reversed(categories))
Now you can get the desired list with
product.all_categories