"prefetch_related" working on only side of One-Many relationship - python

I have two models with one-many relationship (Organization hasMany Locations)
class Organizations(models.Model):
name = models.CharField(max_length=40)
class Location(models.Model):
name = models.CharField(max_length=50)
organization = models.ForeignKey(Organizations, to_field="name", db_column="organization_name", related_name='locations')
class Meta:
db_table = u'locations'
Now, while trying to pre-fetch locations while retrieving "Organizations" I am getting this error.
Organizations.objects.prefetch_related('locations')
AttributeError: Cannot find 'locations' on Organizations object, 'locations' is an invalid parameter to prefetch_related()
However, if I preload the other way around it works fine.
Location.objects.prefetch_related('organization')
Note: After pre-loading organizations from location model, the previous error does not occur anymore.
I am using Django version: 1.8.6

It seems this error is only present in the django shell. This works fine in the django app itself

Related

How to override M2M field names and models in Django with an existing database?

I'm using Django 3.1.3 and working with an existing postgresql database. Most of the models and fields names of this DB are badly chosen and/or way too long. Most of the time its easy to change them with some handy Django options like so :
class NewModelName(models.Models):
new_field_name = models.CharField(max_length=50, db_column='old_field_name')
class Meta:
managed=False
db_table='database_old_table_name'
But let say I want to change a M2M field name and the corresponding model name. I'd like to have something like :
class Foo(models.Models):
new_m2m_field_name = models.ManyToManyField('RelatedModel', blank=True, db_column='old_m2m_field_name')
class Meta:
managed=False
db_table='foo_old_table_name'
class RelatedModel(models.Models):
name = models.CharField(max_length=50)
class Meta:
managed=False
db_table='related_model_old_table_name'
But if I do that, Django will throw an error stating
django.db.utils.ProgrammingError: relation "foo_new_m2m_field_name" does not exist. It is like it is ignoring the db_column option. Any idea how I could get to a similar result ?
Thanks!
From Django documentation regarding ManyToManyField
ManyToManyField.db_table The name of the table to create for storing
the many-to-many data. If this is not provided, Django will assume a
default name based upon the names of: the table for the model defining
the relationship and the name of the field itself.
Also depending on column names (non standard names) in original database you might have to define through model ( pivot table) as through table
You will probably need to manually define the Through model (that Django would otherwise implicitly create behind the scenes) in order to make it unmanaged.
class Foo(models.Models):
new_m2m_field_name = models.ManyToManyField(
"RelatedModel",
blank=True,
db_column="old_m2m_field_name",
through="FooRelatedJoin", # <- new
)
class Meta:
managed = False
db_table = "foo_old_table_name"
class RelatedModel(models.Models):
name = models.CharField(max_length=50)
class Meta:
managed = False
db_table = "related_model_old_table_name"
class FooRelatedJoin(models.Models): # <- all new
foo = models.ForeignKey(Foo)
related_model = models.ForeignKey(RelatedModel)
class Meta:
managed = False
db_table = "foo_join_table"
You could add a property db_table (link)
linking to the previous table, named foo_old_table_name in your case.
According to the doc,
By default, this table name is generated using the name of the
many-to-many field and the name of the table for the model that
contains it
So for the field new_m2m_field_name, the previous table making the link was named : old_field_name_database_old_table_name.
Hence :
new_field_name = models.CharField(max_length=50, db_column='old_field_name', db_table='old_field_name_database_old_table_name')
The option through could be changed too, but I do not think it is necessary if the modifications on names are coherent.

Django Admin: multi-table inheritance child model with many-to-many through inline fails with "Please correct the error below"

I'm not sure if this is a bug, limitation or error on my part, but I would like to get feedback on if there is a solution or work around to this issue...
In Django Admin, I have created a many-to-many inline based on a 'through' model ReleaseCardCount. The inline is linked to a parent class Card which is used for numerous child models. In Django Admin, I am only interested in configuring the child classes of Card, for example, ActionCard.
If I configure Django Admin for the parent Card model and ReleaseCardCount inline, I am able to add, remove and modify parent and inline objects without issue. If I configure the same set up for a child object of card, for example ActionCard, I can add inline objects if there are none or delete all inline objects, but I am unable to modify ActionCard or an inline object if an inline exists. If an inline object is present and I attempt to modify and save any data I get a "Please correct the error below" message, with no error present.
I've minimised the code needed to recreate the issue to the following...
models.py:
from django.db import models
class Release2(models.Model):
release_id = models.AutoField(primary_key=True)
release_name = models.CharField(max_length=100)
def __str__(self):
return self.release_name
class Card2(models.Model):
card_id = models.AutoField(primary_key=True)
card_name = models.CharField(max_length=100)
release_card_count = models.ManyToManyField( Release2, through='ReleaseCardCount2',)
def __str__(self):
return self.card_name
class ReleaseCardCount2(models.Model):
rcc_id = models.AutoField(primary_key=True)
rcc_release = models.ForeignKey(Release2, on_delete=models.CASCADE)
rcc_card = models.ForeignKey(Card2, on_delete=models.CASCADE)
rcc_count = models.PositiveIntegerField(default=1, null=True, blank=True)
class ActionCard2(Card2):
action_card_id = models.AutoField(primary_key=True)
action_name = models.CharField(max_length=100)
def __str__(self):
return self.card_name
admin.py:
from .models import (Release2, Card2, ReleaseCardCount2, ActionCard2)
class ReleaseCardCount2InLine(admin.TabularInline):
model = ReleaseCardCount2
extra = 1
class Card2Admin(admin.ModelAdmin):
inlines = (ReleaseCardCount2InLine,)
model = Card2
class ActionCard2Admin(admin.ModelAdmin):
inlines = (ReleaseCardCount2InLine,)
model = ActionCard2
admin.site.register(Release2)
admin.site.register(Card2, Card2Admin)
admin.site.register(ActionCard2, ActionCard2Admin)
Of interest, in my troubleshooting, I dropped and recreated the database in a dev environment. I found that one of the child models that was not working before, suddenly began to work. I suspect, that it worked due to being the first child model I configured Django Admin for with the inline. To recreate the issue with the above code, you may need try running it FIRST on the parent Card model. Once you have added, saved then modified and saved an inline object on Card, you can try it on ActionCard.
Relevant environment info:
Django - 2.0.6
Python - 3.6.4
PostgreSQL - 10.4

Django: GenericForeignKey and unique_together

In the application I'm working on I'm trying to share access tokens within a company. Example: a local office can use the headquarter's tokens to post something on their Facebook page.
class AccessToken(models.Model):
"""Abstract class for Access tokens."""
owner = models.ForeignKey('publish.Publisher')
socialMediaChannel = models.IntegerField(
choices=socialMediaChannelList, null=False, blank=False
)
lastUpdate = models.DateField(auto_now=True)
class Meta:
abstract = True
Since Facebook, Twitter and other social media sites handle access tokens in their own way I made and abstract class AccessToken. Each site gets its own class e.g.
class FacebookAccessToken(AccessToken):
# class stuff
After doing some reading I found out that I must use a GenericForeignKey to point to classes that inherit AccessToken. I made the following class:
class ShareAccessToken(models.Model):
"""Share access tokens with other publishers."""
sharedWith = models.ForeignKey('publish.Publisher')
sharedBy = models.ForeignKey(User)
# for foreignkey to abstract model's children
contentType = models.ForeignKey(ContentType)
objectId = models.PositiveIntegerField()
contentObject = GenericForeignKey('contentType', 'objectId')
class Meta:
unique_together = (('contentObject', 'sharedWith'))
When I run the django test server I get the following error:
core.ShareAccessToken: (models.E016) 'unique_together' refers to field
'contentObject' which is not local to model 'ShareAccessToken'. HINT:
This issue may be caused by multi-table inheritance.
I don't understand why I get this error, first time using GenericForeignKey. What am I doing wrong?
If there is a smarter way to share the access tokens I would love to hear about it.
Your use of the generic foreign key in this situation is correct.
The error is coming from your unique_together declaration in your model. unique_together can only be used with columns that exist in the database. Since contentObject is not a real column, Django complains about the constraint.
Instead, you can do the following:
unique_together = (('contentType', 'contentId', 'sharedWidth'),)
This is equivalent to what you had defined in your question because contentObject is really just the combination of contentType and contentId behind the scenes.

Django-rest-framework hyperlinkedmodelserializer and foreignkey cities-light

I just installed django-cities-light for my project using DRF and I can't get it work.
My User Model define a city and coutry field as foreignkey, and this is what i get when i tried to get my users :
Could not resolve URL for hyperlinked relationship using view name "city-detail".
You may have failed to include the related model in your API, or incorrectly configured the lookup_field attribute on this field.
Any ideas ?
Thanks !
Using django-cities-light with Rest Framework 3, you need to use their provided view_name:
cities-light-api-{model}-detail
class FooSerializer(HyperlinkedModelSerializer):
url = relations.HyperlinkedIdentityField(view_name="foo-detail")
city = relations.HyperlinkedRelatedField(
view_name="cities-light-api-city-detail", queryset=City.objects.all(),
)
class Meta:
model = Foo
read_only_fields = ('id',)

Retrieve Django rest framework related fields

Using the django-rest-framework is it possible to retrieve content from a related field. So for example I want to create a genre list which contains all projects within it. This is what I have but I keep on getting the error:
'Genre' object has no attribute 'project_set'
models.py
class Genre(models.Model):
name = models.CharField(max_length=100, db_index=True)
class Project(models.Model):
title = models.CharField(max_length=100, unique=True)
genres = models.ManyToManyField(Genre, related_name='genres')
serializers.py
class GenreSerializer(serializers.ModelSerializer):
project_set = serializers.ManyRelatedField()
class Meta:
model = Genre
fields = ('name', 'project_set')
The related name you're using on the Project class is badly named. That related name is how you access the set of projects related to a given genre instance. So you should be using something like related_name='projects'. (As it is you've got it the wrong way around.)
Then make sure that your serializer class matches up with the related name you're using, so in both places project_set should then instead be projects.
(Alternatively you could just remove the related_name='genres' entirely and everything will work as you were expecting, as the default related_name will be 'project_set'.)

Categories