Generic relations vs. iterating on objects in Django - django

I have three models called Post, Category and Flag.
I let users to make a Flag on each Post which is tagged with a Category. Now I want to list all of Flags, related to Posts on a certain Category.
I've found two ways of doing it using ListView generic view:
First approach:
Define a generic relation between Flag and Category so I can call something like: Flag.objects.filter(object__categor=self.category)
Second approach:
def get_queryset(self):
pk = self.kwargs['pk']
self.category = get_object_or_404(Category, pk=pk)
flags = Flag.objects.all()
qs=[]
for f in flags:
if f.object.category == self.category:
qs.append(f)
return qs
My question is which approach is more efficient? I'm not familiar with generic relations, but it seems a little bit heavy to do. On the other hand, querying on all Flag objects and iterating through them isn't cheap one.
Models.py
class Flag(models.Model):
flagger = models.ForeignKey(User, related_name='flaggers')
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = GenericForeignKey('content_type', 'object_id')
submit_date = models.DateTimeField(auto_now_add=True)
reason = models.IntegerField(choices=REASON_CHOICES, default=BROKEN_LINK)
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
...
class Base(models.Model):
title = models.CharField(max_length=200)
slug = models.CharField(max_length=200, db_index=True)
category = models.ForeignKey(Category, default=1)
...
class Text(Base):
description = models.TextField(max_length=500)
...
class Image(Base):
image = models.ImageField()
class Post(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
created_at = models.DateTimeField(null=True)
After saving each of Text or Image instances, I create a Post model. So I can get either Posts flagged or text flagged and image flagged separately.
[also any tip about how can I define generic relation in case I should use it, would be appreciated]

Related

How to use model functions in Views for multiple instances in Django?

I have a Blog Post Model and I have defined a function to calculate the no of likes.
The Model is as follows ->
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
title = models.CharField(max_length=255)
description = models.CharField(max_length=1000,null=True)
Tags = models.CharField(max_length = 255,null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
Likes = models.ManyToManyField(to=User, related_name='Post_likes')
def __str__(self):
return self.title
def likesCount(self):
return self.Likes.count()
Now I am querying the Post Model from the DB to get all the Posts as follows ->
posts = Post.objects.select_related().prefetch_related('images_set','comments_post').annotate(Count('comments_post')).all()
Here when I loop over the posts I can call the likesCount function and it gives me the correct result as well but I want to return the No of likes to the template.
How can I do that?
in your template, try this:
{{ post.likes_set.count }}
and please make the field names lowercase, they are not Classes

Trying to access 'content_object' fields in Django under ContentType

I am trying to access the Item model through the Thing queryset, and keep getting the error:
django.core.exceptions.FieldError: Field 'content_object' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
class ThingContent(models.Model):
content_id = models.AutoField(primary_key=True, null=False)
thing = models.ForeignKey('Thing', on_delete=models.SET_NULL, null=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
I have tried updating the fields on Item by adding a related_query_name to no success.
self.queryset.filter(items__item_date=exact_item_date))
class Item(models.Model):
id = models.AutoField(primary_key=True, null=False)
item_date = models.DateField(**options)
thing_content = GenericRelation('ThingContent', related_query_name='items')
content_object is generic. You must filter by object_id and content_type for know what object you FK generic.
You try again with query like this:
ThingContent.objects.filter(
items__item_date=item_date,
content_type=ContentType.objects.get_for_model(Item)
)
this query mean: find all ThingContent have mapping with Item, and have item_date like what you want filter.

Django inline model formset with 2 models

First of all, please forgive for my newbie questions. I did copy most of the code, and try to understand from Django documents.
Code as below:
models.py
class Order(models.Model):
ORDER_CHOICES = (
('import', 'IMPORT'),
('export', 'EXPORT')
)
storage = models.ForeignKey(Storage, on_delete=models.PROTECT)
order_type = models.CharField(max_length=6, choices=ORDER_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class Item(models.Model):
def random_barcode():
return str(random.randint(10000000, 99999999))
type = models.ForeignKey(Type, on_delete=models.CASCADE)
order = models.ForeignKey(Order, on_delete=models.CASCADE, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
item_name = models.CharField(max_length=50, help_text='Name of goods, max 50 characters')
barcode = models.CharField(max_length=8, default=random_barcode, unique=True)
production_date = models.DateField()
expired_date = models.DateField()
def __str__(self):
return self.item_type
forms.py
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ['order',]
fields = ['type', 'brand', 'item_name', 'production_date', 'expired_date']
ItemFormSet = inlineformset_factory(Order, Item, form=ItemForm, extra=1)
views.py
class CreatePO(CreateView):
model = Order
context_object_name = 'orders'
template_name = 'storages/create_po.html'
fields = ['order_type', 'storage',]
*#dun't know how to write below code....*
1st question: how to use inline formset to write the CreatePO view?
2nd question: I need my create PO template as below picture, how to add a "Quantity" field?
This kind of template need Javascript, right? Any alternative solution? I have no knowledge with javascript.
First of all, move the def random_barcode(): before def __str__(self): it looks so ugly formated code.
Then let's have a look in your pic, if you haven't proper experience with Javascript you can use Admin Views from Django, it's much more simple and supported by Django 2.1. Read more if you would like to give permission to everyone in a admin-views page https://docs.djangoproject.com/el/2.1/releases/2.1/#model-view-permission
So quantity will be just added inside Item class
quantity = models.PositiveSmallIntegerField(default=1)
Also for your form, in my opinion, you need modelform_factory, so I suggest to read this one https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#modelform-factory-function

Django: Is it possible to limit ContentType by base Parent?

I have an Abstract base class "Parent" from which derive two subclasses "Child1" and "Child2". Each child can have a set of "Status".
I used "ContentType", "GenericForeignKey" and "GenericRelation" like so:
from django.db import models
from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Parent(models.Model):
name = models.CharField(max_length=30, blank=True)
class Meta:
abstract = True
def __str__(self):
return self.name
class Child1(Parent):
id_camp = models.PositiveIntegerField()
config_type = models.CharField(max_length=30)
status_set = GenericRelation(Status)
class Child2(Parent):
temperature = models.FloatField(null=True, blank=True)
status_set = GenericRelation(Status)
class Status(models.Model):
code = models.CharField(max_length=10, null=True, blank=True)
message = models.CharField(max_length=100, null=True, blank=True)
content_type = models.ForeignKey(ContentType, limit_choices_to={'name__in': ('child1', 'child2',)}, null=True, blank=True)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
The actual solution works fine, but right now the limitation of choices of content type is by "name" and eventually I will create more subclacess of Parent later. I would like to replace limit_choices_to={'name__in': ('child1', 'child2',)} with somthing like limit_choices_to children of parent is there any straightforward way ?
limit_choices_to also accepts callables, so yes, a dynamic value should be possible:
Either a dictionary, a Q object, or a callable returning a dictionary or Q object can be used.
So something along these lines should work:
def get_children():
return {'model__in': [c.__name__ for c in Parent.__subclasses__()]}
and then later ...
limit_choices_to=get_children
Or even in one line:
limit_choices_to={'model__in': [c.__name__ for c in Parent.__subclasses__()]}

Generic Relation Constraints in Django

I want the a counterpart of Tag (BlogPost) to have at least 1 instance of Tag or it shouldn't be created. (same effect like null=False). I tried a lot but couldn't figure out to apply these contrains. Any ideas?
class Tag(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
text = models.CharField("text", max_length=255)
class Meta:
unique_together = ('content_type', 'object_id', 'text',)
class BlogPost(models.Model):
title = models.CharField("title", max_length=255)
tags = generic.GenericRelation(Tag, verbose_name="tags")
class TagInline(generic.GenericTabularInline):
model = Tag
extra = 1
class BlogPostAdmin(admin.ModelAdmin):
inlines = (TagInline,)
If you want this in the form of a Database constraint, then I'm not sure that such a thing exists.
Otherwise I would go with overriding the clean( self ) function on your model.
This can be used for custom validation.
def clean( self ):
# validate that this model has one or more tag