Django: To combine and retrieve rows from different models - django

1.We have two similar models, and
I would like to be able to retrieve these at the same time and sort them by posting date and time, etc.
Is it possible?
2.Or should both redundant fields be combined into one model?
# 1.
class Model1(models.Model):
title = ...
thumbnail = ...
description = ...
...
class Model2(models.Model):
title = ...
image_url = ...
product_id = ...
reivew = ...
# 2. Combine into one model
class Model(models.Model)
title = ...
thumbnail = ...
description = ...
image_url = ...
product_id = ...
reivew = ...

You can union() two models with no shared parents, and after that order_by() some
column.

'Consider this answer from a beginner'
If you just want to fetch someones model1 and model2 objects with just one query statement maybe is good to inherit two models from a base model like this:
in your models.py:
class Base(models.Model):
title = ...
created_at = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(user)
class Meta:
ordering = ['-created_at']
# inherits fields of Base model
class Model1(Base):
field = ...
# fields
class Model2(Base):
# fields
using this method, remember fill needed fields of `Base' model like this:
>>> m1 = Model1(field="value", title=..., owner=UserObject,...).save()
# for get objects do normal:
>>> objects = Base.objects.filter(owner=user)
# the result is list of users `Model1' or 'Model2` created objects
Also you can use django-model-utils and get Base objects by child type. It would be like this:
from model_utils.managers import InheritanceManager
class Base(models.Model):
# like previous version
# just remember modify objects
objects = InheritanceManager()
# inherits fields of Base model
class Model1(Base):
class Model2(Base):
get objects:
>>> Base.objects.all().select_related('Model1', 'Model2')
please read also this answer and others.

Related

Make django-admin-sortable and django-parler work together

I have two models with many-to-many relation using through table
# models.py
class Item(TranslatableModel):
name = models.CharField(max_length=100) # this one is translatable
class Person(TranslatableModel):
name = models.CharField(max_length=100) # this one is translatable
items = models.ManyToManyField(
'Item',
through='PersonItem',
)
class PersonItem(Sortable):
class Meta(Sortable.Meta):
ordering = ['order']
person = models.ForeignKey('Person')
item = SortableForeignKey('Item')
But I am not able to have sorted Items in admin
# admin.py
class PersonItemInline(TranslatableTabularInline, SortableTabularInline):
model = PersonItem
form = PersonItemForm
extra = 1
class PersonAdmin(TranslatableAdmin):
form = PersonForm
inlines = [
PersonItemInline
]
admin.site.register(Person, PersonAdmin)
When I inherit from translatable first I lose sorting feature. When I'm trying to inherit from sortable first I have following error
NoReverseMatch Reverse for 'person_person_do_sorting' with arguments
'(239,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
Env (python 3):
django==1.10.7, django-parler==1.6.5, django-admin-sortable==2.0.19
#most-wanted ensure that the inhreritance order is correct:
class PersonItemInline(SortableTabularInline, TranslatableTabularInline):
...
class PersonAdmin(SortableAdmin, TranslatableAdmin):
...
and override change_form_template_extends in PersonAdmin class like this:
class PersonAdmin(SortableAdmin, TranslatableAdmin):
change_form_template_extends = 'admin/parler/change_form.html'

Django annotation on related model

Having these models (simplified):
class UserProfile(models.Model):
user = models.OneToOneField(User)
products = models.ManyToManyField(Product, through='UserProduct')
class Product(models.Model):
title = models.CharField(max_length=100, blank=False)
class UserProduct(models.Model):
user = models.ForeignKey(UserProfile)
product = models.ForeignKey(Product)
class Recipe(models.Model):
ingredients = models.ManyToManyField(Product, through='Ingredient')
class Ingredient(models.Model):
product = models.ForeignKey(Product)
recipe = models.ForeignKey(Recipe)
I need in some cases to get a list of recipes, marked on each ingredient, "whether it is user have that product.". And, maybe other calculated fields, according to given user.
Example of what i want:
>>> Recipe.objects.get_for_user(user=user)[0].ingredients[0].is_user_have
>>> True
But, of course, in other cases i don't want that field attached to ingredients.
I understand that the i need custom manager. But straightforward solution - add "is_user_have" as property to Ingredient model, define custom manager with get_for_user method, call base get_queryset and then in for-loop populate that field - doesn't work.
UPDATE 1
I figured out how to get annotations that i wanted, here is my custom manager for ingredients:
class UserIngredientsManager(models.Manager):
def get_queryset(self):
result = super(UserIngredientsManager, self).get_queryset()
return (result
.annotate(
user_have_count=models.Count(
models.Case(
models.When(
# Hardcoded !!!
product__userproduct__user_id=1,
then=True),
output_field=models.IntegerField())))
.annotate(
is_user_have=models.Case(
models.When(
user_have_count__gt=0,
then=models.Value(True)),
output_field=models.BooleanField(),
default=models.Value(False))))
But there are two problems:
I can't pass user to this manager (its hardcoded for testing)
I can't create proxy model for situtations when i want this annotations (see below), it only works when i replace default manager on Ingredient model.
This code doesn't work, for ingredients default related manager used instead:
class RecipeWithUserInfo(Recipe):
class Meta:
proxy = True
objects = UserRecipesManager()
ingredients = UserIngredientsManager()
It works only when i replace default manager on Ingredient model (but that not what i want):
class Ingredient(models.Model):
...
objects = UserIngredientsManager()

Serialize relation both ways with Django rest_framework

I wonder how to serialize the mutual relation between objects both ways with "djangorestframework". Currently, the relation only shows one way with this:
class MyPolys(models.Model):
name = models.CharField(max_length=20)
text = models.TextField()
poly = models.PolygonField()
class MyPages2(models.Model):
name = models.CharField(max_length=20)
body = models.TextField()
mypolys = models.ManyToManyField(MyPolys)
# ...
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
class MyPages2Serializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPages2
# ...
class MyPolyViewSet(viewsets.ReadOnlyModelViewSet):
queryset = testmodels.MyPolys.objects.all()
serializer_class = srlz.MyPolysSerializer
class MyPages2ViewSet(viewsets.ReadOnlyModelViewSet):
queryset = testmodels.MyPages2.objects.all()
serializer_class = srlz.MyPages2Serializer
The many-to-many relation shows up just fine in the api for MyPages2 but nor for MyPolys. How do I make rest_framework aware that the relation goes both ways and needs to be serialized both ways?
The question also applies to one-to-many relations btw.
So far, from reading the documentation and googling, I can't figure out how do that.
Just do it like this:
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
fields =('id','name','text','poly')
class MyPages2Serializer(serializers.HyperlinkedModelSerializer):
mypolys = MyPolysSerializer(many=True,read_only=True)
class Meta:
model = testmodels.MyPages2
fields =('id','name','body','mypolys')
I figured it out! It appears that by adding a mypolys = models.ManyToManyField(MyPolys) to the MyPages2 class, Django has indeed automatically added a similar field called mypages2_set to the MyPolys class, so the serializer looks like this:
class MyPolysSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = testmodels.MyPolys
fields = ('name', 'text', 'id', 'url', 'mypages2_set')
I found out by inspecting an instance of the class in the shell using ./manage.py shell:
pol = testmodels.MyPolys.objects.get(pk=1)
pol. # hit the tab key after '.'
Hitting the tab key after the '.' reveals additional fields and methods including mypages2_set.

Django Rest Framework - Reverse relations

how do you include related fields in the api?
class Foo(models.Model):
name = models.CharField(...)
class Bar(models.Model):
foo = models.ForeignKey(Foo)
description = models.CharField()
Each Foo has a couple of Bar's related to him, like images or what ever.
How do I get these Bar's displaying in the Foo's resource?
with tastypie its quit simple, im not sure with Django Rest Framework..
I got it working! Shweeet!
Ok this is what I did:
Created serializers, Views and URLS for the Bar object as described in the Quickstart docs of Django REST Framework.
Then in the Foo Serializer I did this:
class FooSerializer(serializers.HyperlinkedModelSerializer):
# note the name bar should be the same than the model Bar
bar = serializers.ManyHyperlinkedRelatedField(
source='bar_set', # this is the model class name (and add set, this is how you call the reverse relation of bar)
view_name='bar-detail' # the name of the URL, required
)
class Meta:
model = Listing
Actualy its real simple, the docs just dont show it well I would say..
These days you can achieve this by just simply adding the reverse relationship to the fields tuple.
In your case:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = (
'name',
'bar_set',
)
Now the "bar"-set will be included in your Foo response.
I couldn't get the above working because I have a model called FooSomething.
I found the following worked for me.
# models.py
class FooSomething(models.Model):
name = models.CharField(...)
class Bar(models.Model):
foo = models.ForeignKey(FooSomething, related_name='foosomethings')
description = models.CharField()
# serializer.py
class FooSomethingSerializer(serializers.ModelSerializer):
foosomethings = serializers.StringRelatedField(many=True)
class Meta:
model = FooSomething
fields = (
'name',
'foosomethings',
)

Django filters - Using an AllValuesFilter (with a LinkWidget) on a ManyToManyField

This is my first Stack Overflow question, so please let me know if I do anything wrong.
I wish to create an AllValues filter on a ManyToMany field using the wonderful django-filters application. Basically, I want to create a filter that looks like it does in the Admin, so I also want to use the LinkWidget too.
Unfortunately, I get an error (Invalid field name: 'operator') if I try this the standard way:
# Models
class Organisation(models.Model):
name = models.CharField()
...
class Sign(models.Model):
name = models.CharField()
operator = models.ManyToManyField('Organisation', blank=True)
...
# Filter
class SignFilter(LinkOrderFilterSet):
operator = django_filters.AllValuesFilter(widget=django_filters.widgets.LinkWidget)
class Meta:
model = Sign
fields = ['operator']
I got around this by creating my own filter with the many to many relationship hard coded:
# Models
class Organisation(models.Model):
name = models.CharField()
...
class Sign(models.Model):
name = models.CharField()
operator = models.ManyToManyField('Organisation', blank=True)
...
# Filter
class MyFilter(django_filters.ChoiceFilter):
#property
def field(self):
cd = {}
for row in self.model.objects.all():
orgs = row.operator.select_related().values()
for org in orgs:
cd[org['id']] = org['name']
choices = zip(cd.keys(), cd.values())
list.sort(choices, key=lambda x:(x[1], x[0]))
self.extra['choices'] = choices
return super(AllValuesFilter, self).field
class SignFilter(LinkOrderFilterSet):
operator = MyFilter(widget=django_filters.widgets.LinkWidget)
I am new to Python and Django. Can someone think of a more generic/elegant way of doing this?
Why did you subclass LinkOrderFilterSet?
Maybe the connect way to use it is this:
import django_filters
class SignFilter(django_filters.FilterSet):
operator = django_filters.AllValuesFilter(widget=django_filters.widgets.LinkWidget)
class Meta:
model = Sign
fields = ['operator']
You can use this
class CircleFilter(django_filters.FilterSet):
l = []
for c in Organisation.objects.all():
l.append((c.id, c.name))
operator = django_filters.ChoiceFilter(
choices=set(l))
class Meta:
model = Sign
fields = ['operator']