Django - using Foreign key mode attribute - python

I have two Django models and connected via Foreignkey element and in the second model I need to use the firs model's attribute - example (pseudocode):
Class Category(models.Model):
c_attribute = "Blue"
Class Object(models.Model):
o_category = models.ForeignKey(Category)
o_color = o_category.c_attribute
The key here is the last line - I got error saying that ForeignKey object has no attribute c_attribute.
Thanks

Because o_category is a Key to a Category, not a Category itself!
you can check by type(o_category) to check is not Category!
so you have access to related Cateogry of a Object in other parts of application when connected to database.for example in shell you can write:
c = Category()
c.save()
o = Object(o_category = c, ...) #create Object with desired params
... #some changes to o
o.save()
o.o_category.c_attribute #this will work! :)

You can use to_field='', but that might give you an error as well.
Class Object(models.Model):
o_category = models.ForeignKey(Category)
o_color = models.ForeignKey(Category, to_field="c_attribute")
The best thing is do create a function in your Object model, that would get you the categories c_attribute like so:
def get_c_attribute(self):
return self.o_category.c_attribute

Related

How to use prefetch_related on two M2M values?

I want to prefetch_related to two level of M2M values,
Here is my models.py
class A(models.Model):
name = models.CharField(max_length=40)
b = models.ManyToManyField('B')
class B(models.Model):
name = models.CharField(max_length=40)
c = models.ManyToManyField('C')
class C(models.Model):
name = models.CharField(max_length=40)
d = models.ManyToManyField('D')
And my ORM is
a_obj = A.objects.all().prefetch_related('a__b__c')
And I am trying to access the values like below,
Method A:
for each_obj in a_obj:
print(each_obj.a__b__c)
Method B:
for each_obj in a_obj:
print(each_obj.a.all())
Method A throws an error saying No such value a__b__b for A found
Method B doesn't throw any error, but the number of queries increases to the length of a_obj.
Is there a way to access a__b__c in a single query?
You load both the related B and C models with .prefetch_related(…) [Django-doc]:
a_objs = A.objects.prefetch_related('b__c')
But here .prefetch_related(…) does not change how the items look, it simply loads items. You thus can access these with:
for a in a_objs:
for b in a.b.all():
for c in b.c.all():
print(f'{a} {b} {c}')
You this still access the items in the same way, but here Django will already load the objects in advance to prevent extra queries.

Find model instance from table name in Django

I've the following table.
class TempTable(models.Model):
name = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
...
...
class Meta:
db_table = 'temp_table'
Now suppose I know the table name temp_table. Currently I'm using the following script to get the model instance from table name.
from django.db.models import get_app, get_models
all_models = get_models()
for item in all_models:
if item._meta.db_table == 'temp_table':
return item
Are there any better ways of achieving this..??
you can access the model instance from the model name like below also you check the table name but i think you do not need this;
if hasattr("TempTable", "__class__"):
mod = __import__("<your_app_name>.models", fromlist=["TempTable"])
nclss = getattr(mod, "TempTable")
# in here nclss is your model instance so you can use it
# nclss.objects.all().order_by("id")
return nclss
# you can also check the table name but i think you did not need this
if nclss._meta.db_table == "temp_table":
return nclss
i hope it works for you

Django ManyToManyField Error when saving in admin?

What is wrong with my code?
class Group(ImageModel):
title = models.CharField(verbose_name = "Title", max_length=7)
photos = models.ManyToManyField('Photo', related_name='+',
verbose_name=_('Photo'),
null=True, blank=True)
.....
pid = Photo.objects.get(image = str_path)
gid= Group.objects.get(id = self.id)
self.save_photos(gid, pid)
....
def save_photos(self, gid, pid):
group_photo = GroupPhotos(groupupload=gid.id,
photo=pid.id
)
group_photo.save()
and my GroupPhotos models is:
class GroupPhotos(models.Model):
groupupload = models.ForeignKey('Group')
photo = models.ForeignKey('Photo')
class Meta:
db_table = u'group_photos'
when i want to save it from admin panel i am getting value error sth like this:
Cannot assign "38": "GroupPhotos.groupupload" must be a "Group" instance.
with group_photo = GroupPhotos(groupupload=gid, photo=pid) defination it is working but there is no any changes in GroupPhotos table(group_photos). printing this print pid.id,' >>> ',gid.id i am getting true relation...
UPDATE:
I have been working since morning, but no progress... i have also tried this but nothing changed:
pid = Photo.objects.get(image = str_path)
ger = Group.objects.get(id = self.id)
ger.title = self.title
ger.save()
ger.photos.add(pid)
The error is here:
group_photo = GroupPhotos(groupupload=gid.id, photo=pid.id)
The arguments to groupupload and photo should be instances of Group and Photo respectively. Try the following:
group_photo = GroupPhotos(groupupload=gid, photo=pid)
In other words, when creating an object you need to pass arguments of the expected type and not an integer (which may be the primary key key of the desired object but it also might not, which is why you need to pass an object of the correct type).
i have solved my problem with adding through option to my manytomanyfield:
photos = models.ManyToManyField('Photo', related_name='+',
verbose_name=_('Photo'),
null=True, blank=True, through=GroupPhotos)
some info about ManyToManyField.through here:
Django will automatically generate a table to manage many-to-many
relationships. However, if you want to manually specify the
intermediary table, you can use the through option to specify the
Django model that represents the intermediate table that you want to
use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.

Indexing not supported?

I get this error message:
TypeError: 'City' object does not support indexing
when this is my model:
class City(db.Model):
region = db.ReferenceProperty()
name = db.StringProperty()
image = db.BlobProperty()
vieworder = db.IntegerProperty()
areacode = db.IntegerProperty()
and this is my query
items = Item.all().filter('modified >', timeline).filter('published =', True).order('-modified').filter('cities =',city[0].key()).fetch(PAGESIZE + 1)`
Can you tell my how I should make my query or model my class definitions? I use listproperty(db.Key) to model relationships with references and I thought I could filter like the above query since it worked for one item. What should I do?
city[0].key()
is the cause of your error. Chances are you thought city was a list, but it's actually a single item, or some such.

How does django one-to-one relationships map the name to the child object?

Apart from one example in the docs, I can't find any documentation on how exactly django chooses the name with which one can access the child object from the parent object. In their example, they do the following:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __unicode__(self):
return u"%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place, primary_key=True)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
def __unicode__(self):
return u"%s the restaurant" % self.place.name
# Create a couple of Places.
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()
# Create a Restaurant. Pass the ID of the "parent" object as this object's ID.
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()
# A Restaurant can access its place.
>>> r.place
<Place: Demon Dogs the place>
# A Place can access its restaurant, if available.
>>> p1.restaurant
So in their example, they simply call p1.restaurant without explicitly defining that name. Django assumes the name starts with lowercase. What happens if the object name has more than one word, like FancyRestaurant?
Side note: I'm trying to extend the User object in this way. Might that be the problem?
If you define a custom related_name then it will use that, otherwise it will lowercase the entire model name (in your example .fancyrestaurant). See the else block in django.db.models.related code:
def get_accessor_name(self):
# This method encapsulates the logic that decides what name to give an
# accessor descriptor that retrieves related many-to-one or
# many-to-many objects. It uses the lower-cased object_name + "_set",
# but this can be overridden with the "related_name" option.
if self.field.rel.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
return None
return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
else:
return self.field.rel.related_name or (self.opts.object_name.lower())
And here's how the OneToOneField calls it:
class OneToOneField(ForeignKey):
... snip ...
def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(),
SingleRelatedObjectDescriptor(related))
The opts.object_name (referenced in the django.db.models.related.get_accessor_name) defaults to cls.__name__.
As for
Side note: I'm trying to extend the
User object in this way. Might that be
the problem?
No it won't, the User model is just a regular django model. Just watch out for related_name collisions.

Categories

Resources