Django reverse lookup by ForeignKey - python

I have a django project which has two apps, one is AppA and AppB. Now AppA has a model
ModelA which is referenced by the model ModelB in AppB, using modelA = models.ForeignKey(ModelA, related_name='tricky')
Now in my view for AppA, when it shows ModelA, I do a get_object_or_404(ModelA, pk=prim_id). Then I want to get all the ModelBs which have a Foreign Key pointing to ModelA.
Documentation says I should do a mb = ModelB.objects.get(pk=prim_id) then mb.modela_set.all()
But, it failed on the mb.modela_set, and it says "ModelB object has no attribute 'suchsuch'". Notice I added the related_name field to ForeignKey, so I tried with that as well, including mb.tricky.all() and mb.tricky_set.all() to no avail.
Oh, and I have specified a different manager for AppA where I do objects = MyManager() which returns the normal query but with a filter applied.
What could be the problem? What is the prefered way to get the ModelBs referencing ModelA?

If the ForeignKey is, as you describe in ModelB and you do mb = ModelB.objects.get(pk=prim_id) then the look up for the modela attribute is not a reverse lookup. you simply access the related object via mb.modela!

Related

In Django, when trying to access a model's 1:1 relation that doesn't exist, sometime that relation is None, and sometimes it raises an exception. Why?

I noticed while debugging a Django application that with two tables with a 1:1 relation, trying to access the related object from the table that defines the 1:1 relation would return None, while trying to access the same nonexistent relation from the table that does not define the relation Django will raise a Model1.model2.RelatedObjectDoesNotExist exception. I was confused by this behavior as the same action has two different results on objects on the same type of relation.
This is an example of the models used:
class RelatedModel(models.Model):
pass
class RelationshipDefiner(models.Model):
related_object = models.OneToOneField(
RelatedModel,
on_delete=models.CASCADE,
related_name="relationship_definer",
blank=True,
null=True
)
This is an example of trying to print the relations to screen:
relationship_definer = RelationshipDefiner.objects.create()
related_object = RelatedModel.objects.create()
print("RELATIONSHIP DEFINER'S RELATED OBJECT:")
print(relationship_definer.related_object)
print("RELATED OBJECT'S RELATIONSHIP DEFINER:")
print(related_object.relationship_definer)
And the output:
RELATIONSHIP DEFINER'S RELATED OBJECT:
None
RELATED OBJECT'S RELATIONSHIP DEFINER:
Internal Server Error: /test/
File "/django/app/views/test.py", line 17, in test
print(related_object.relationship_definer)
app.models.sample.RelatedModel.relationship_definer.RelatedObjectDoesNotExist: RelatedModel has no relationship_definer.
Why do 1:1 nullable relationships behave differently on either side of the relation, and is there a way to coerce Django to behave consistently across both sides of the relationship?

Peewee and Flask-Admin: View with only foreign keys missing attribute '_data'

I use flask peewee together with flask-admin and it works fine for some model classes, but I got one model, that it does not work on and gives me the following error:
AttributeError: 'GroupMember' object has no attribute '_data'
This is how the model looks:
class GroupMember(BaseModel):
group = ForeignKeyField(Group)
member = ForeignKeyField(Member)
class Meta:
primary_key = CompositeKey('group', 'member')
As this model consists of only foreign keys, I thought I would need to create a custom view that lists the columns explicitly, because by default flask-admin hides those columns.
I did so:
class MyModelView(ModelView):
column_list = ('group', 'member')
And initialized the view:
admin.add_view(MyModelView(GroupMember))
This honestly changed nothing and I still get the same error. I found related questions that mentioned "backrefs" - what would that do and how would I do this? Or is this even the reason for my problem?

Using '_id' in Django

I am a bit confused how Django handles '_id' property when we use ORM with some models that use foreign key.
For example:
class CartItem(models.Model):
user = models.ForeignKey('accounts.CustomUser', related_name='carts', on_delete=models.CASCADE, verbose_name='User')
product = models.ForeignKey('pizza.Product', related_name='carts', on_delete=models.CASCADE, verbose_name=_('Product'))
quantity = models.SmallIntegerField(verbose_name=_('Quantity'))
And when I use ORM with 'filter' I can easily use something like:
CartItem.objects.filter(user=1, product=1, quantity=1)
And Django kind of 'see' that I refer to 'id', but when I use exacly the same line of code, but with 'create' instead of 'filter':
CartItem.objects.create(user=1, product=1, quantity=1)
Then it throws an error saying:
Cannot assign "1": "CartItem.user" must be a "CustomUser" instance.
And to create it I need to use:
CartItem.objects.create(user_id=1, product_id=1, quantity=1)
Why is that? Is there some rule here that I don't understand?
This is the database representation of the ForeignKey [Django-doc]. A reference to model object is represented as:
Behind the scenes, Django appends "_id" to the field name to create its database column name. In the above example, the database table for the Car model will have a manufacturer_id column. (You can change this explicitly by specifying db_column) However, your code should never have to deal with the database column name, unless you write custom SQL. You’ll always deal with the field names of your model object.
So you could say that Django will construct a "twin" column, with an _id suffix. This column has the same type as the type of the primary key of the model you target, and that column will thus contain the primary key of the model object you use. Note that you can use a different field to which you target by specifying the to_field=… parameter [Django-doc].
The ForeignKey itself thus does not exists at the database, that is the logic of Django that will use the primary of that object, and store that in a column with, by default, an _id suffix.

how to create a relation model with forms?

Hey i need to create a model from form. It's different way if i need to create a some another model, that have a relations with created object model from form. its must work like that -> i come on site, get form for create a model object and save new object. an then -> i have another model with relations with model from form. and i need, to create relation model object automaticly - when django taked new object from forms.
tried any can help me. i make it this way, but this time i have problem. -> i have manytomany field in my relation model, and i have manytomany field ->users from form. and i cant create a relation model with this instance :( Traceback:
TypeError at /ru/center/add/
Direct assignment to the forward side of a many-to-many set is prohibited. Use users_center.set() instead.
but i tired to try it(( help please, how i may do it?
views.py
for x in form['users_center'].data:
name_center = form['name_center'].data
fullname_center = form['fullname_center'].data
ogrn = form['ogrn_center'].data
head_center = form['head_center'].data # user id
many_users = form['users_center'].data
user_center = x # user id
new_center = Center.objects.create(name_center=name_center,
fullname_center=fullname_center,
ogrn_center=ogrn,
head_center=User.objects.get(id=head_center),
users_center=User.objects.get(id=int(x)))
new_center.save()
models.py
users_center = models.ManyToManyField(User,
related_name='center',
# through=CenterDetails,
default=None, blank=True,
verbose_name=_("Сотрудники"))
There is a join table implied by the many-to-many relationship between two models. The error is letting you know that you must set the relationship after creating a User object instead of trying to assign the relationship while creating the User object. You can use set() or add() to do this.
So try doing:
new_center = Center.objects.create(
name_center=name_center,
fullname_center=fullname_center,
ogrn_center=ogrn,
head_center=User.objects.get(id=head_center),
)
users_center=User.objects.get(id=int(x))
new_center.users_center.add(users_center)
Additionally it may be useful to rename your many to many field as a plural to indicate the relationship. Maybe users_centers instead. Since it seems like users can have many centers, and centers can have many users. That's up to you though, not required for it to work.

Django merging 2 model instances issue with m2m fields which specifies an intermediary model

I'm using a script to automate merging 2 Django model instances (so it can be used for any kind of model in my system). But there is a problem when it comes to the part that handle m2m fields which specifies an intermediary model (using through parameter when this field defined), I get the error: AttributeError: Cannot use remove() on a ManyToManyField which specifies an intermediary model. Use <MyModel>'s Manager instead
# Migrate all many to many references from alias object to primary object.
for related_many_object in get_all_related_many_to_many_objects(alias_object):
alias_varname = related_many_object.get_accessor_name()
obj_varname = related_many_object.field.name
if alias_varname is not None:
# standard case
related_many_objects = getattr(alias_object, alias_varname).all()
else:
# special case, symmetrical relation, no reverse accessor
related_many_objects = getattr(alias_object, obj_varname).all()
for obj in related_many_objects.all():
getattr(obj, obj_varname).add(primary_object) #This line raises the AttributeError
getattr(obj, obj_varname).remove(alias_object)
Here is the function that return all m2m field of the object which need to be merged:
def get_all_related_many_to_many_objects(obj):
return [
f for f in obj._meta.get_fields(include_hidden=True)
if f.many_to_many and f.auto_created
]
I have 2 question about this case:
Is there any built-in functions that allows me to distinguish m2m fields which specifies intermediary model from m2m fields which doesn't? (Because with the normal m2m fields this add() function seemingly works well).
With m2m fields which specifies, how can I gracefully remove/add a model instance to that relationship?
Thank for your time!

Categories