I have these models
class Collection(models.Model):
items = models.ManyToManyField('Item', through='CollectionItem', related_name='collection_items')
...
class Item(models.Model):
...
class CollectionItem(models.Model):
collection = models.ForeignKey(Collection)
item = models.ForeignKey(Item)
extra_stuff = models.CharField(max_length=100)
Am i thinking about this the wrong way or should i be able to access the 'through' objects related to a collection, using
collection = get_object_or_404(Collection, pk=1)
collection.collectionitem_set.all()
As i'm just getting an an attribute error?
However I am am able to access them through the related_name
collection = get_object_or_404(Collection, pk=1)
collection.collection_items.all()
Am i missing something really obvious? Why doesn't using 'collectionitem_set' work?
EDIT
Just noticed an important omission, which is why collection.collection_items.all() works. I had the same related_name set on the CollectionItem as well
class CollectionItem(models.Model):
collection = models.ForeignKey(Collection, related_name='collection_items')
item = models.ForeignKey(Item)
extra_stuff = models.CharField(max_length=100)
Once i've removed that, neither way works.
I can't repeat that. Using your models, with additional name fields:
>>> from models import Collection, Item, CollectionItem
>>> c = Collection.objects.create(name='coll1')
>>> c.collectionitem_set.all()
[]
Related
I'm new in using GenericForeignKey, and I couldn't make it to work in a query statement. The tables are roughly like the following:
class Ticket(models.Model):
issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
issue_id = models.PositiveIntegerField(null=True, blank=True)
issue = generic.GenericForeignKey('issue_ct', 'issue_id')
class Issue(models.Model):
scan = models.ForeignKey(Scan)
A scan creates one issue, an issue generates some tickets, and I made Issue as a foreign key to Ticket table. Now I have a Scan object, and I want to query for all the tickets that related to this scan. I tried this first:
tickets = Tickets.objects.filter(issue__scan=scan_obj)
which doesn't work. Then I tried this:
issue = Issue.objects.get(scan=scan_obj)
content_type = ContentType.objects.get_for_model(Issue)
tickets = Tickets.objects.filter(content_type=content_type, issue=issue)
Still doesn't work. I need to know how to do these kind of queries in django? Thanks.
The Ticket.issue field you've defined will help you go from a Ticket instance to the Issue it's attached to, but it won't let you go backwards. You're close with your second example, but you need to use the issue_id field - you can't query on the GenericForeignKey (it just helps you retrieve the object when you have a Ticket instance). Try this:
from django.contrib.contenttypes.models import ContentType
issue = Issue.objects.get(scan=scan_obj)
tickets = Ticket.objects.filter(
issue_id=issue.id,
issue_ct=ContentType.objects.get_for_model(issue).id
)
Filtering across a GenericForeignKey can by creating a second model that shares the db_table with Ticket. First split up Ticket into an abstract model and concrete model.
class TicketBase(models.Model):
issue_ct = models.ForeignKey(ContentType, related_name='issue_content_type')
issue_id = models.PositiveIntegerField(null=True, blank=True)
class Meta:
abstract = True
class Ticket(TicketBase):
issue = generic.GenericForeignKey('issue_ct', 'issue_id')
Then create a model that also subclasses TicketBase. This subclass will have all the same fields except issue which is instead defined as a ForeignKey. Adding a custom Manager allows it to be filtered to just a single ContentType.
Since this subclass does not need to be synced or migrated it can be created dynamically using type().
def subclass_for_content_type(content_type):
class Meta:
db_table = Ticket._meta.db_table
class Manager(models.Manager):
""" constrain queries to a single content type """
def get_query_set(self):
return super(Manager, self).get_query_set().filter(issue_ct=content_type)
attrs = {
'related_to': models.ForeignKey(content_type.model_class()),
'__module__': 'myapp.models',
'Meta': Meta,
'objects': Manager()
}
return type("Ticket_%s" % content_type.name, (TicketBase,), attrs)
I am very new to Django and would appreciate your help with this problem:
I have a table with meta-informationen (like the number of clicks, votes, comments ...) for different areas of my website (news, events in the calendar, films ..). The table is referenced by two primary keys (fi = INTEGER and tbl = CHAR).
class News(models.Model):
title = models.CharField()
...
class Film(models.Model):
title = models.Charfield()
...
class Calendar(models.Model):
title = models.Charfield()
...
class MetaInfo(models.Model):
fi = ForeignKey(??) # Integer
tbl = ForeignKey(??) # CharField
Example: fi = 1 and tbl = 'news' would relate to news-entry with primary key 1. And fi = 100, tbl = 'film' would relate to film-entry with primary key 100.
How to implment this? Is this even possible with django?
Django has a built-in feature called Generic Foreign Keys that allow you to tie a single table to multiple models in this fashion.
This is how you would create your models:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class News(models.Model):
title = models.CharField()
...
class Film(models.Model):
title = models.Charfield()
...
class Calendar(models.Model):
title = models.Charfield()
...
class MetaInfo(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
If you wish, you can be more explicit about the relationship between a model and MetaInfo by using GenericRelation. For example:
class Film(models.Model):
title = models.CharField()
metainfo = generic.GenericRelation('MetaInfo')
...
This allows you to access the related MetaInfo records directly from the Film model, as in
f = Film.objects.get(pk=1)
for mi in f.metainfo.all():
#mi is a matching MetaInfo record for Film record with pk=1
Just to elaborate a bit:
In the MetaInfo model, content_type serves as the equivalent of your tbl column (although it points to a Django construct called a ContentType; Django constructs one for each model in the app/set of apps) and object_id corresponds to your fi key. You actually generally don't pay much attention to those fields. Instead, you get and set the content object, which is the corresponding record. For example, instead of storing or retrieving tlb='Film', fi=1, you'd get or set content_object which corresponds directly to the Film record matching pk=1.
In essence, contenttype__name='Film', object_id=1 while content_object=Film.object.get(pk=1)
This is all assuming that this database is for Django use only. If it's an existing database that you're trying to use within Django, there isn't a straightforward way to handle this that I'm aware of.
I want to perform a filter on a model and return all objects that have a specific attribute.
model.objects.filter(hasattr(model, 'attrname'))
This obviously doesn't work, but not sure how to efficiently implement something siilar.
Thanks
EDIT
An example of where I would use this is when a model is inherited from another
class model1(models.Model):
...
class model2(model1):
...
if I do a model1.objects.all() each of the returned objects that are in model2 will have an extra attribute
If the models are related, you can the isnull in the filter.
model1.objects.filter('related_name__field_name__isnull=False)
where related name is in for the foreign key in model2
For Example:
class Owner(models.Model):
user = models.CharField(max_length=10)
class Car(models.Model):
car_type = models.CharField(max_length=10)
owner = models.ForeignKey(Owner, related_name='cars',
on_delete=models.CASCADE)
For owners with cars:
owners = Owner.objects.filter(cars__id__isnull=False)
I just put it in a:
try:
....
except AttributeError:
....
The way I did it was to suppress the FieldError exception:
from django.core.exceptions import FieldError
from contextlib import suppress
with suppress(FieldError):
model.objects.filter(field_in_other_class=value)
hope that helps
How to maintain insert ordering with one-to-many Django mapping, for eg:
Say we have,
class Person(models.Model):
name = Model.CharField(max_length=50)
class Subject(models.Model):
sub_name = Model.CharField(max_length=50)
person = Model.ForeignKey('Person')
def insert_data():
person = Person.objects.create(name='Deepan')
Subject(name='Eng', person=person).save()
Subject(name='Sci', person=person).save()
Subject.objects.filter(person=person) # is there a way to make this to always return the subjects in the inserted order, i.e Eng, Sci instead of Sci, Eng
This is handled using list types in hibernate/grails
Define the ordering using meta info:
class Subject(models.Model):
sub_name = models.CharField(max_length=50)
person = models.ForeignKey('Person')
time = models.DateTimeField(auto_now_add = True)
class Meta:
ordering = ['time'] #or ['-time'] according to the ordering you require
This will save the creation datetime in the time field and hence the results will be ordered according to addition time.
btw (if there are some other reasons) from your models it seems there will be many Persons and many Subjects so I suggest using many to many fields. This will map multiple users to multiple subjects and symmetrically back. You may even use the through option to store more details (time etc for sorting, even marks/percentage for storing records if you require to do that) per Person-Subject mapping.
You need to use a meta class to always sort it and use a date time field with the auto_now option set to True.
I recommend using a proxy class for the sorting. Sorting is an expensive operation.
See this link for more details on sorting and this link on proxy models.
class Meta:
ordering = ['id']
And no additional fields required. Django model always has 'id' field.
I was looking for exactly the same and to order_with_respecto_to works:
class Game(models.Model):
title = models.CharField(max_length=255)
class Task(models.Model):
title = models.CharField(max_length=255)
game = models.ForeignKey(Game, on_delete=models.CASCADE)
class Meta:
order_with_respect_to = 'game'
You can see the original order as they where inserted and you can modify it if you want. Also you can get the next register in sequence
>>> from prueba.models import Game,Task
>>> a=Game.objects.get(id=1)
>>> a.get_task_order()
<QuerySet [1, 2, 3]>
>>> a.set_task_order([3,2,1])
>>> a.get_task_order()
<QuerySet [3, 2, 1]>
>>> t=Task.objects.get(id=2)
>>> t.get_next_in_order()
<Task: task1>
>>> t=Task.objects.get(id=3)
>>> t.get_next_in_order()
<Task: task2>
Here is the documentation
https://docs.djangoproject.com/en/3.0/ref/models/options/#order-with-respect-to
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']