Django- Get Foreign Key Model - django

How can I Get A Foreign Key Model Type?
For Example:
class Category(models.Model):
name = models.CharField(max_length = 100)
class SubCategory(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length = 100)
I Want To Get category Model In SubCategory.
How Can I Do It?

Try:
subcategory = SubCategory.objects.get(pk=given_pk)
subcategory.category
EDIT:
subcategory._meta.get_field('category').rel.to

For Django>=2.0
>>> SubCategory._meta.get_field('category').remote_field.model
>>> 'my_app.models.Category'
To get the model name use the __name__ class property.
>>> SubCategory._meta.get_field('category').remote_field.model.__name__
>>> 'Category'

ForeignKeys are ReverseSingleRelatedObjectDescriptor objects. So that's what you are really working with. You'll get that if you run type(SubCategory.category). From here you can use two ways to get the actual Class/Model referred to.
SubCategory.category.field.rel.to # <class 'path.to.Model'>
SubCategory.category.field.rel.to.__name__ # 'Category'
# or these will do the same thing
SubCategory._meta.get_field('category').rel.to
SubCategory._meta.get_field('category').rel.to.__name__
If you don't know the attribute name until run-time, then use getattr(SubCategory, attributeNameVariable) to get your ReverseSingleRelatedObjectDescriptor object for that ForeignKey field.

also for django > = 2.0
>>> SubCategory._meta.get_field('category').related_model
>>> <class 'my_app.models.Category'>
>>> SubCategory._meta.get_field('category').related_model._meta.model_name
>>> 'category'

Related

Django Foreign Key Aliasing

I'm trying to be able to alias a column name from a model's foreign key. I want to be able to change 'owner__username' to just 'username' when passing a JSON response.
query_n = Example.objects.values('owner__username','name')
print(query_n[0])
Which prints
{'name': 'e_adam', 'owner__username': 'adam'}
The only renaming of a column I have seen was via annotate() however, that isn't even truly a column (AVG, SUM, ...)
The Example model has a foreign key owner, which is 'auth.User' model.
Any thoughts?
I'm not too sure about this but if you are using django >= 1.7, sounds like you could use annotate to create alias for named arguments in values(). I found a related django ticket, to quote from the latest response:
Just quickly, with the changes to annotate that have landed, it is now
possible to create aliases yourself, and reference them from the
values call:
from django.db.models import F
Model.objects.annotate(my_alias=F('some__long__name__to__alias')) \
.values('my_alias')
aliasName = models.ForeignKey(modelName, to_field='fieldName', on_delete=models.CASCADE)
This is the format for aliasing in Django Foreign Key
Here is full Models page
from django.db import models
# Create your models here.
class Farm(models.Model):
srFarm = models.AutoField(primary_key=True)
name = models.CharField(max_length = 264, unique =True)
address = models.CharField(max_length = 264)
def __str__(self):
temp = '{0.name},{0.address}'
return temp.format(self)
class Batch(models.Model):
srBatch = models.AutoField(primary_key=True)
farmName = models.ForeignKey(Farm, to_field='name', on_delete=models.CASCADE)
def __str__(self):
temp = '{0.farmName}'
return temp.format(self)

Django self-referential foreign key with related_name doesn't work

I followed this question to create a model pointing to itself with a ForeignKey. I just added the related_name option. The model looks like this:
class Person(models.Model):
name = models.CharField(max_length=100)
best_friend = models.ForeignKey('self', blank=True, null=True,
related_name='considered_best_friend_for')
def __unicode__(self):
return self.name
Then I did the following:
>>> a = Person.objects.create(name='Albert')
>>> a.save()
>>> j = Person.objects.create(name='John')
>>> j.save()
>>> m = Person.objects.create(name='Mark')
>>> m.save()
>>> a.best_friend = m
>>> a.save()
>>> j.best_friend = m
>>> j.save()
>>> m.considered_best_friend_for.all()
[]
>>> m.considered_best_friend_for
<django.db.models.fields.related.RelatedManager object at 0x102503190>
I don't understand why when I query m.considered_best_friend_for.all() I get an empty result []. I was expecting to get [<Albert>, <John>]. However, if I query m.considered_best_friend_for I get the RelateManager object.
Where is the mistake?
Note: I tried the same code changing the ForeignKey for a ManyToMany relationship and it worked, but in this case I need a ForeignKey relationship because a person can be considered best friend for more than one person (or zero), but can only consider one (or zero) person his/her best friend.
Thank you!
Mistake found! I was using a custom manager (needed for another part of the code) that was filtering out the results. Sorry about that! :)

Why will my django manytomany through only allow access through related_name?

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()
[]

Django many-to-many relationship with extra fields

I am building a simple interface to a biological database using the django-admin to populate the db. I want tot to use a many-to-many relationship for a questionnaire to fish species (one questionnaire can have more than one species and one species can be present in more than one questionnaire). The two models in question:
class Species(models.Model):
fish_spp_name = models.CharField(max_length=255, unique=True)
class Questionaire(models.Model):
# ...
fish_caught = models.ManyToManyField(Species)
now, I want to my data to contain a number of each species caught, per questionnaire. So, for example, I can associate 3 different species with questionnaire id=1, but how do I include that, say 2 of the first species, 1 of the second and 4 of the third were caught?
Check this: Extra fields on many-to-many relationships
Define another models Caught to hold the information per catch. Give it a related_name to make it easier to refer to in your code. You might also want to unique_together appropriate fields.
class Species(models.Model):
name = models.CharField(max_length=255, unique=True)
def __unicode__(self):
return '%s/%d' % self.name
class Questionaire(models.Model):
pass
class Caught(models.Model):
species = models.ForeignKey(Species)
number = models.IntegerField()
questionaire = models.ForeignKey(
Questionaire, related_name='catches')
def __unicode__(self):
return '%s/%d' % (self.species.name, self.number)
Use it like this:
(InteractiveConsole)
>>> from app.models import *
>>> s1 = Species(name='Salmon')
>>> s1.save()
>>> s2 = Species(name='Mackerel')
>>> s2.save()
>>> q = Questionaire()
>>> q.save()
>>> c1 = Caught(species=s1, number=7, questionaire=q)
>>> c2 = Caught(species=s2, number=5, questionaire=q)
>>> c1.save()
>>> c2.save()
>>> q.catches.all()
[<Caught: Salmon/7>, <Caught: Mackerel/5>]
>>>

How to maintain insert ordering for one-to-many relationship in Django

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