Get backward relationships items for model in Django - python

I have a pet project with reviews about spare parts for cars.
Here is models.py:
class CarBrand(models.Model):
brand = models.CharField(max_length=40, choices=(), unique=True, verbose_name="Марка")
def __str__(self):
return self.brand
def get_absolute_url(self):
return reverse_lazy('car_models_all', kwargs={'car_id': self.pk})
class CarModel(models.Model):
model_name = models.CharField(max_length=60, db_index=True, verbose_name="Модель")
brand_id = models.SmallIntegerField(verbose_name="ID марки")
def __str__(self):
return self.model_name
def get_absolute_url(self):
return reverse_lazy('model_info', kwargs={'model_id': self.pk})
class SparePartCategory(models.Model):
name = models.CharField(max_length=255, db_index=True, verbose_name='Название')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse_lazy('spare_parts_category', kwargs={'category_id': self.pk})
class SparePart(models.Model):
name = models.CharField(max_length=255, db_index=True, verbose_name='Название')
brand = models.CharField(max_length=255, db_index=True, verbose_name="Производитель")
number = models.CharField(max_length=30, db_index=True, verbose_name="Номер (артикул)")
category = models.ForeignKey(SparePartCategory, on_delete=models.PROTECT, verbose_name="Категория")
def __str__(self):
return ' '.join([self.name, self.brand, self.number])
def get_absolute_url(self):
return reverse_lazy('get_spare_part', kwargs={'spare_part_id': self.pk})
class Review(models.Model):
RATING_VALUES = [
('1', 'Ужасно'), ('2', 'Плохо'), ('3', 'Сносно'), ('4', 'Хорошо'), ('5', 'Отлично'),
]
spare_part = models.ForeignKey(SparePart, on_delete=models.PROTECT, verbose_name="Запчасть")
mileage = models.SmallIntegerField(verbose_name="Пробег, тыс.км")
car_brand = models.ForeignKey(CarBrand, on_delete=models.PROTECT, verbose_name="Марка авто")
car_model = models.ForeignKey(CarModel, on_delete=models.PROTECT, verbose_name="Модель авто")
owner = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Владелец")
rating = models.CharField(max_length=1, choices=RATING_VALUES, verbose_name="Рейтинг", default=3)
review = models.TextField(max_length=1000, blank=True, verbose_name="Отзыв")
def __str__(self):
return ' '.join([self.spare_part.name, self.spare_part.brand, self.spare_part.number])
In views.py I get one SparePart
def get_spare_part(request, spare_part_id):
spare_part = get_object_or_404(SparePart, pk=spare_part_id)
installed_on_cars = # here I need to get all CarModel for current SparePart with Review exists
context = {
'spare_part': spare_part,
'installed_on_cars': installed_on_cars,
}
return render(request, 'mileage/spare_part.html', context)
I need to get all CarBrand and CarModel for current SparePart with Review exists, and get hyperlinks for template for them. How do I can to implement this?
I think I need to make it with backward relationships, but can not understand how.

Use the _set manager object (documentation here).
for review in spare_part.review_set.all():
print(f'brand: {review.care_brand}')
print(f'model: {review.car_model}')
If you define a related_name (e.g. 'reviews) for spare_parts in Review, you can use that name for backward query instead of _set (e.g. spare_part.reviews.all()).

Related

Linkify Foreign Keys with Django Tables2 and Slugs

I am trying to build summary tables with clickable links that drill down to lower levels. The hierarchy in descending order is Division -> Region -> Branch -> Profit Center where each profit center can have multiple "receivables." I want the division summary view to show all of the regions (e.g. California, Central Texas, Ohio, etc) and a sum of the 'total_amount' for the regions.
I want to be to click a region and it drills down to the branches where I can see a summary for each branch. So on and so forth
I've spent far longer than I would like to admit on this and the lines commented out on tables.py is just a fraction of what I have tried.
What am I missing?
class Division(models.Model):
name = models.CharField(max_length=128, unique=True)
slug = models.SlugField(unique=True, null=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('division-detail', args=[str(self.slug)])
class Region(models.Model):
name = models.CharField(max_length=128, unique=True)
division = models.ForeignKey('Division', to_field='name', on_delete=models.CASCADE)
slug = models.SlugField(unique=True, null=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('region-summary', args=[str(self.slug)])
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
class Branch(models.Model):
name = models.CharField(max_length=128)
number = models.CharField(max_length=4, unique=True)
region = models.ForeignKey('Region', to_field='name', on_delete=models.CASCADE)
slug = models.SlugField(unique=True, null=True)
def __str__(self):
return self.number
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.number)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('branch-detail', args=[str(self.slug)])
class Profit_Center(models.Model):
branch = models.ForeignKey('Branch', on_delete=models.CASCADE)
number = models.CharField(max_length=32, unique=True, editable=False, null=True, blank=True)
slug = models.SlugField(unique=True, null=True)
class Receivable(models.Model):
document = models.IntegerField(primary_key=True, blank=False)
payer = models.ForeignKey('Payer', on_delete=models.PROTECT)
profit_center = models.ForeignKey(Profit_Center, to_field = 'number', on_delete=models.PROTECT, null=True)
total_amount = models.DecimalField(decimal_places=2, max_digits=20, null=True, blank=True)
slug = models.SlugField(unique=True, null=True, editable=False)
tables.py
#summary by selected division, showing summary by region
class ReceivableDivisionSummaryHTMxTable(tables.Table):
#profit_center__branch__region = tables.Column(linkify=('region-summary', {'slug': 'central-texas'})) #always goes to central texas
#profit_center__branch__region = tables.Column(linkify=('region-summary', {'region': tables.A('region'), 'slug': tables.A('region.slug')})) #fails on accessor key
#profit_center__branch__region = tables.Column(linkify=('region-summary', {'slug': tables.A('profit_center__branch__region.slug')}))
#profit_center__branch__region = tables.Column(linkify=('region-summary', {'slug': tables.A('name')})) #fails on accessor key
#profit_center__branch__region = tables.LinkColumn('region-summary', args=[A('profit_center__branch__region.slug')])
#profit_center__branch__region = tables.Column(linkify=True)
#profit_center__branch__region = tables.LinkColumn('region-summary', kwargs={'slug': 'record.slug'}) #half-ass works, returns regionsummary/region_slug insteald of the real slug
#profit_center__branch__region = tables.LinkColumn('region-summary', args=[A('pk')])
#profit_center__branch__region = tables.Column(linkify=('region-summary', {'pk': tables.A('region__pk')})) #half-ass works, returns regionsummary/region_slug insteald of the real slug
#profit_center__branch__region = tables.LinkColumn('region-summary', args=[A('slug')])
#profit_center__branch__region = tables.Column(linkify=('region-summary', {'slug': tables.A('region.slug')}))
#species =tables.TemplateColumn('{{value}}')
profit_center__branch__region = tables.TemplateColumn('{{value}}') #sooooo fucking close, returns the division- (e.g. Ford) rather region (e.g. Ohio), though it displays Ohio correctly as the value
class Meta:
model = Receivable
template_name = 'tables/bootstrap_htmx.html'
fields = ('profit_center__branch__region', 'sum_col')
views.py
def ReceivableDivisionSummaryHTMxTableView(request, slug):
#post = get_object_or_404(Receivable, slug=slug)
queryset = Receivable.objects.values('profit_center__branch__region').annotate(sum_col=Sum('total_amount'))
table= ReceivableDivisionSummaryHTMxTable(queryset)
context = {'table': table, 'slug': slug}
if request.htmx:
template_name = "app/receivable_table_partial.html"
else:
template_name = 'app/receivable_summary_htmx.html'
template_name
return render(request, template_name, context)
def ReceivableRegionSummaryHTMxTableView(request, slug):
queryset = Receivable.objects.values('profit_center').annotate(sum_col=Sum('total_amount'))
table= ReceivableRegionSummaryHTMxTable(queryset)
context = {'table': table, 'slug':slug}
if request.htmx:
template_name = "app/receivable_table_partial.html"
else:
template_name = 'app/receivable_summary_htmx.html'
template_name
return render(request, template_name, context)
Update: I've reduced it to the absolute bare minimum and I am still running into the error: for linkify=True, '100100' must have a method get_absolute_url
100100 is a profit center
models.py
class Profit_Center(models.Model):
branch = models.ForeignKey('Branch', on_delete=models.CASCADE)
number = models.CharField(max_length=32, unique=True, editable=False, null=True, blank=True)
profit_center_slug = models.SlugField(unique=True, null=True)
def get_absolute_url(self):
return reverse('branch-detail', args=[str(self.profit_center_slug)])
class Receivable(models.Model):
document = models.IntegerField(primary_key=True, blank=False)
profit_center = models.ForeignKey(Profit_Center, to_field = 'number', on_delete=models.PROTECT, null=True)
total_amount = models.DecimalField(decimal_places=2, max_digits=20, null=True, blank=True)
receivable_slug = models.SlugField(unique=True, null=True, editable=False)
tables.py
class AllProfitCenterSummaryHTMxTable(tables.Table):
profit_center = tables.Column(linkify=True)
class Meta:
model = Receivable
template_name = 'tables/bootstrap_htmx.html'
fields = ('profit_center', 'sum_col', )
views.py
def AllProfitCenterSummaryHTMxTableView(request):
queryset = Receivable.objects.values('profit_center').annotate(sum_col=Sum('total_amount'))
table= AllProfitCenterSummaryHTMxTable(queryset)
if request.htmx:
template_name = "app/receivable_table_partial.html"
else:
template_name = 'app/receivable_summary_htmx.html'
template_name
return render(request, template_name, {'table':table})
urls.py
path('htmx/allprofitcentersummary/', views.AllProfitCenterSummaryHTMxTableView, name='all-profit-center-summary'),
Update 2: getting closer. I've got it half working with the below code:
class ReceivableDivisionSummaryHTMxTable(tables.Table):
class Meta:
model = Receivable
template_name = 'tables/bootstrap_htmx.html'
fields = ('profit_center__branch__region', 'sum_col',)
def render_profit_center__branch__region(self, value):
return format_html('{}', value, value)
However, it is taking the text value of the field (e.g. Central Texas). However, I need it to be slugified (e.g. central-texas). I tried to do it with the following, but it isn't taking.
def render_profit_center__branch__region(self, value):
value_slug = value
slugify(value_slug)
return format_html('{}', value_slug, value)
Can I pass a slug into the render?
Update 3: I've got the slugification to work with the following:
def render_profit_center__branch__region(self, value):
return format_html('{}', slugify(value), value)
However, I still need to pass a slug into the render. I want my links to formatted as divisionsummay/NAMEOFDIVSION/regionsummary/NAMEOFREGION
I need to pass a slug back to the render similar to the following:
def render_profit_center__branch__region(self, value, division_slug):
return format_html('{}', division_slug, slugify(value), value)
Obviously, that isn't working. Not sure how to pass the division_slug.
Update 4: went a different route. Instead of having divisionsummay/NAMEOFDIVSION/regionsummary/NAMEOFREGION, I am simply going to have separate urls (e.g. app/htmx/divisionsummary/NAMEOFDIVISON, app/htmx/regionsummary/NAMEOFREGION, etc).
Achieved via the following:
def render_profit_center__branch__region(self, value):
return format_html('{}', reverse_lazy('region-summary', args=[slugify(value)]), value)

Error 'Product' object has no attribute '_combinator_query'

I am creating an e-commerce website in django where the homepage can be changed dynamically by changing the instances of FeaturedProduct objects. These objects have two ForeignKeys. First is to the Product class and the other is to FeaturedCategory class(this is for the type of deal, eg: daily deals, new arrivals, etc). So, I'm trying to get the Product instances in a queryset instead of the FeaturedProduct queryset. When I union two querysets, I get an error that 'Product' object has no attribute '_combinator_query'. The codes have been shown below.
My models.py
class Product(models.Model):
product_id = models.AutoField
product_name = models.CharField(max_length=50, unique=True)
category = models.ForeignKey(Category, on_delete=models.SET_DEFAULT, default='1')
sub_category = models.ForeignKey(SubCategory, on_delete=models.SET_DEFAULT, default='2')
price = models.IntegerField(default=0)
stock = models.IntegerField(default=0)
desc = models.TextField()
pub_date = models.DateField()
avg_rating = models.IntegerField(default=0)
avg_rating_float = models.FloatField(default=0.0)
number_of_ratings = models.IntegerField(default=0)
image1 = models.ImageField(upload_to="images", default="")
image2 = models.ImageField(upload_to="images", default="")
image3 = models.ImageField(upload_to="images", default="")
slug = models.SlugField(blank=True, help_text=('Leave this parameter empty, it will get generated automatically.'))
def save(self, *args, **kwargs):
self.slug = slugify(self.product_name)
super(Product, self).save(*args, **kwargs)
def __str__(self):
return self.product_name
class FeaturedCategory(models.Model):
category = models.CharField(max_length=100)
def __str__(self):
return self.category
class Meta:
verbose_name_plural = "Featured Categories"
class FeaturedProduct(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
featured_category = models.ForeignKey(FeaturedCategory, on_delete=models.CASCADE)
def __str__(self):
return self.product.product_name
My views.py
def index(request):
prod = Product.objects.none()
feat = FeaturedProduct.objects.all()
for pro in feat:
ob = Product.objects.get(product_name=pro.product)
print(ob.product_name)
print(ob.category)
print(ob.sub_category)
print(ob.price)
prod.union(ob) # Error found here
# Working part
products = Product.objects.all()
context = {'products': products}
return render(request, 'index.html', context)
The last three lines are working fine, but not the other part of the index function.
Please help. Thank You.

Many to Many field Returning None django

When I try to print a field (Many-to-Many)in the terminal I only get the result "None". Am I missing something?
view.py
def assign_skill (request, pk):
plan = get_object_or_404(Plan, pk=pk)
if(request.GET.get('assign_skill')):
print("nummer1")
print(plan.title)
print(plan.skillonplan)
return redirect('all-plan')
models.py
class Plan(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, editable=False)
subscriber = models.ManyToManyField(User, blank = True, related_name = 'plansubscriber')
title = models.CharField(max_length=100, default = '', null=True, blank=True)
description = models.TextField(max_length=10000, default = '', null=True, blank=True)
company = models.CharField(max_length=200, null=True, editable=False)
created_date = models.DateTimeField(default=timezone.now)
skillonplan = models.ManyToManyField(Skill, null=True, blank = True, related_name='planneke')
duration = models.IntegerField(default=0,
validators=[MaxValueValidator(36), MinValueValidator(1)]
)
def get_skills(self):
return '\n'.join([p.skills for p in self.skill.all()])
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse_lazy('plan_detail', kwargs={'pk': self.id})
you can use following code, in order to return many to many fields :
print(plan.skillonplan.all())

Django: When saving issue

I's using django 1.6 and have a model for a blog but when saving the content I get category id can not be blank but I don't understand this error. I have tried looking at the code trying different things but it doesn't seem to be working.
class Category(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
description = models.TextField(null=True, blank=True)
class Meta:
verbose_name_plural = "Categories"
def __unicode__(self):
return '%s' % self.title
#permalink
def get_absolute_url(self):
return ('view_category', None, {'slug': self.slug})
class Blog(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=100, unique=True)
description = models.TextField(max_length=2000)
extended = models.TextField(null=True, blank=True)
category = models.ForeignKey(Category)
created = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
class Meta:
verbose_name = 'Blog post'
verbose_name_plural = 'Blog posts'
ordering = ('-updated',)
def __unicode__(self):
return '%s' % self.title
#permalink
def get_absolute_url(self, ):
return('view_questions', None, {'slug': self.slug,})
def save(self):
super(Blog, self).save()
if not self.slug:
self.slug = '%d/%s' % (
self.pk, slugify(self.title)
)
super(Blog, self).save()
when saving the content I get category id can not be blank but I don't
understand this error.
category = models.ForeignKey(Category)
That line means that a blog post must belong to a category. In your blog table, the category foreign key is called category_id.
You can either make sure to add a category; blog.category = Category.objects.get(...) for example OR you can make the category optional:
category = models.ForeignKey(Category, blank=True, null=True)

How to display a foreign key fields in the objects listing of the Django admin?

i have the following models setup
class Player(models.Model):
#slug = models.slugField(max_length=200)
Player_Name = models.CharField(max_length=100)
Nick = models.CharField(max_length=100, blank=True)
Jersy_Number = models.IntegerField()
Team_id = models.ForeignKey('Team')
Postion_Choices = (
('M', 'Manager'),
('P', 'Player'),
)
Poistion = models.CharField(max_length=1, blank=True, choices =Postion_Choices)
Red_card = models.IntegerField( blank=True, null=True)
Yellow_card = models.IntegerField(blank=True, null=True)
Points = models.IntegerField(blank=True, null=True)
#Pic = models.ImageField(upload_to=path/for/upload, height_field=height, width_field=width, max_length=100)
class PlayerAdmin(admin.ModelAdmin):
list_display = ('Player_Name',)
search_fields = ['Player_Name',]
admin.site.register(Player, PlayerAdmin)
class Team(models.Model):
"""Model docstring"""
#slug = models.slugField(max_length=200)
Team_Name = models.CharField(max_length=100,)
College = models.CharField(max_length=100,)
Win = models.IntegerField(blank=True, null=True)
Loss = models.IntegerField(blank=True, null=True)
Draw = models.IntegerField(blank=True, null=True)
#logo = models.ImageField(upload_to=path/for/upload, height_field=height, width_field=width, max_length=100)
class Meta:
pass
#def __unicode__(self):
# return Team_Name
#def save(self, force_insert=False, force_update=False):
# pass
#models.permalink
def get_absolute_url(self):
return ('view_or_url_name')
class TeamAdmin(admin.ModelAdmin):
list_display = ('Team_Name',)
search_fields = ['Team_Name',]
admin.site.register(Team, TeamAdmin)
my question is how do i get to the admin site to show Team_name in the add player form Team_ID field currently it is only showing up as Team object in the combo box
You are almost there, you have commented it out and forgot to call the attribute properly:
def __unicode__(self):
return self.Team_Name
Read the documentation.
And for updated developers (Python 3.x):
def __str__(self):
return self.Team_name
Add a unicode method to the team object:
def __unicode__(self):
return self.Team_name

Categories