Prefetch queryset when related_name="+" - django

Is it possible without related name (related_name="+") to prefetch objects on the target instance? Sure I know it's not a problem with the related name, but I'm not really sure if it's possible without it.
Here is the example code:
from django.db import models
class Parent(models.Model):
name = models.CharField(max_length=50)
class Child(models.Model):
parent = models.ForeignKey(to=Parent, related_name="+", on_delete=models.CASCADE)
name = models.CharField(max_length=50)
Parent.objects.all().prefetch_related('child_set')
Maybe it's possible using the Prefetch(lookup, queryset=None, to_attr=None) object, because it takes the queryset in the argument list?

Looked through the code a bit and found this line:
rel_obj_descriptor = getattr(instance.__class__, through_attr, None)
Here instance is the model instance, and through_attr is the field name of related instance to be fetched. This line basically tries to get a related descriptor to perform the prefetch query. In your case rel_obj_descriptor would contain None.
To answer your question no it is not possible at least for a Foreign Key, there may be some hack for Many to Many relationships as Django appears to use some internal descriptors for them.
I would advice you to simply not set related_name="+" since you want to use the backwards relation here. You say "It's because of separation of concerns between multiple apps" but that does not make much sense. Don't we set a foreign key to the user model for various other models anyway and still use the related name? Does the point of separation of concerns arise there(the user model is in a separate app)?

try
parent = Parent.objects.get(id=pk)
parent.child_set.all()
I don't know if having related_name = '+' prevents this situation, but if you never define related_name, you can definitely use this method.

Related

bulk create in django with foreign key

Models:
class Author(Base):
name = models.CharField(max_length=100, unique=True)
class Book(Base):
name = models.CharField(max_length=100, unique=True)
class AuthorBookAssn(Base):
author = models.ForeignKey(Author, on_delete=models.PROTECT)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
I have an api to create a book, and along with the book data we would also get a list of author ids.
Now for each book we need to create one/more records (depending on the author ids provided) in the AuthorBookAssn table.
What is the best way to do this and can the create be done in bulk.
Currently the approach is to get the author objects for each of the ids in the list and then call
AuthorBookAssn.objects.create(book=book_instance,author=author_instance)
You've created a many-to-many relationship so your current method is the only possible way based on your current structure. If you were to use Django's in-built m2m field then you would essentially do the same except you would do something like author.books.add(book), but again, you would have to do this separately to your book/author creation. An alternative would be to use a many-to-one relation (i.e. ForeignKey field) which would allow you to connect the two when an object is created. Many-to-One might not be how you want to structure things if books can have multiple authors and vice-versa.
(supplementary to OsVoid's answer)
There might be some degree of optimization by working with the object ids (primary key values) rather than fetching the entire objects. Premature optimization is a bad idea, and you'd have to benchmark this idea to see if any improvement is measurable (assuming you have any need to optimize at all).
Given book_pk and author_pk you can use the "magic" _id suffix:
AuthorBookAssn.objects.create(book_id=book_pk,author_id=author_pk)
And instead of fetching whole objects, you might fetch just their pk values using a .values_list('pk') in a queryset. (with flat=True if only the one value is being requested). Since this is just a number, it also might be possible to attach it to some other objects that you really do need to obtain, using annotation.
Also, you can cause your own model to be used for the association in a Django ManyToMany relation, using "through". This is valuable if you want to store extra information about the association, such as when it was created, who by, for what purpose, etc.

Get all related objects of an object in Django?

Let us say I have a model which contains related (foreign key) fields. Likewise, those Foreign Key fields may refer to models which may or may not contain related fields. Note that relational fields in Django may be one-to-one, many-to-one, or many-to-many.
Now, given an instance of a model, I want to recursively and dynamically get all instances of the models related to it, either directly or indirectly down the line. Conceptually, i want to perform a traversal of the related objects and return them.
Example:
class Model1{
rfield1 = models.ForeignKey("Model2")
rfield2 = models.ManyToManyField("Model3")
normalfield1 = models.Charfield(max_length=50)
}
class Model2{
sfield = models.ForeignKey("Model3")
normalfield = models.CharField(max_length=50)
}
class Model3{
normalfield = models.CharField(max_length=50)
}
Let's say, I have an instance of model Model1 model1, and I want to get objects directly related to it i.e. all Model2 and Model3 objects, and also those which are indirectly related i.e. all Model3 objects related to the Model2 objects retrieved previously. I also want to consider the case of a One-to-One field where the related field is defined on the OTHER MODEL.
Also, note that it might not be the case that I know the model of an instance I'm currently working on. Let's say in the previous example, I may not now that model1 is an instance of Model1 model. So I want to perform all these dynamically.
In order to this, I think I need a way to get all related fields of an object.
How to get all the related fields of an object?
And how should I use them to get the actual related objects?
Or is there a way to better to do this? Thank you very much!
UPDATE:
I already know how to perform 1, and 2 basically follows directly from 1. :) Update later.
If you have model1 getting all it's many to many field names (etc) is easy since this is well know and these are all stored in the meta's 'local_many_to_many' list:
[field.name for field in model1._meta.local_many_to_many]
The foreign keys are a bit more tricky since they are stored with all other fields in the meta's 'local_fields' list. Hence we need to make sure that it has a relation of sorts. This can be done as follows:
[field.name for field in model1._meta.local_fields if field.rel]
This method has requires no knowledge of your models. Also further interrogation can be done on the field object if the name is not enough.

How do you decide on creating a new model vs a field in django?

I'm creating a user profile class for my new django website, and I am trying to decide how to represent a user's physical address in my models.
Is it better practice to create a new subclass of model and reference it with a OneToOne key like
class UserProfile(models.Model):
...
address = models.OneToOneField(AddressModel)
...
class AddressModel(models.Model)
street_address = models.CharField(max_length=30)
city = models.CharField(max_length=15)
....
or is it better to create a new address field like
class UserProfile(models.Model):
...
address = AddressField(location_dict)
...
class AddressField(models.Field)
# details go here
...
I generally find it useful to have separate models if the entries might be created independently. For example, if you might end up with a collection of addresses AND a collection of users, not all of which will be linked immediately, then I'd keep them separate.
However, if all addresses in your database will always and immediately be associated with a user, I'd simply add a new field to the model.
Note: some people will tell you that it's wrong and evil to have nullable database columns, and that you should therefore have a separate model if any of your addresses will ever be None. I disagree; while there are often many great reasons to avoid nullable columns, in cases like this I don't find the inconvenience of checking for a null address any more onerous than checking whether the one-to-one model entry exists.
Like Eli said, it's a question of independence. For this particular example, I would make the address a field of UserProfile, but only if you expect to have one address per user. If each user might have multiple addresses (a home address and a vacation address, for example), then I would recommend setting up a model using ForeignKey, which models a Many-To-One relationship.
class UserProfile(models.Model):
...
class AddressModel(models.Model)
user = models.ForeignKey(UserProfile)
street_address = models.CharField(max_length=30)
city = models.CharField(max_length=15)
location = models.CharField(max_length=15) #"Home," "work," "vacation," etc.
Then many AddressModel objects can be created and associated with each UserProfile.
To answer your question, I'd say in general it's probably better to separate out the address as mentioned by other users.
I think the more you learn about database normalization the easier this question is to answer.
This article, Using MySQL, Normalisation, should help you figure out the basics of the "forms" of normalization. BTW, even though it's titled MySQL, it's really very generic for relational databases.
While you don't always need to go through all the normal-forms for all projects, learning about it really helps.

Making a fairly complex Django model method sortable in admin?

I have a reasonably complex custom Django model method. It's visible in the admin interface, and I would now like to make it sortable in the admin interface too.
I've added admin_order_field as recommended in this previous question, but I don't fully understand what else I need to do.
class Book(models.Model):
id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=200)
library_id = models.CharField(max_length=200, unique=True)
def current_owner(self):
latest_transaction = Transaction.objects.filter(book=self)[:1]
if latest_transaction:
if latest_transaction[0].transaction_type==0:
return latest_transaction[0].user.windows_id
return None
current_owner.admin_order_field = 'current_owner'
Currently, when I click on the current_owner field in the admin interface, Django gives me
FieldError at /admin/books/book/
Cannot resolve keyword 'current_owner' into field
Do I need to make a BookManager too? If so, what code should I use? This isn't a simple Count like the example in the previous question, so help would be appreciated :)
Thanks!
The Django admin won't order models by the result of a method or any other property that isn't a model field (i.e. a database column). The ordering must be done in the database query, to keep things simple and efficient.
The purpose of admin_order_field is to equate the ordering of a non-field property to the ordering of something that is a field.
For example, a valid values current_owner.admin_order_field could be id, title or library_id. Obviously none of these makes sense for your purpose.
One solution would be to denormalise and always store current_owner as a model field on Book; this could be done automatically using a signal.
You can't do this. admin_order_field has to be a field, not a method - it's meant for when you have a method that returns a custom representation of an underlying field, not when you do dynamic calculations to provide the value. Django's admin uses the ORM for sorting, and that can't sort on custom methods.

unique_together foreign key object properties

I've got two models: Common and ARecord. ARecord has a ForeignKey relationship to Common. I want to ensure that ARecord is unique with a combination of items from ARecord and Common.
class Common(models.Model):
NAIC_number = models.CharField(max_length=5)
file_location_state = models.CharField(max_length=2)
file_location_code = models.CharField(max_length=2)
class ARecord(models.Model):
common = models.ForeignKey(Common)
coverage_code = models.CharField(max_length=6)
record_type = models.CharField(max_length=1)
class Meta:
unique_together = ('coverage_code', 'common__NAIC_number')
However, when I attempt to access the foreign key object property via the usual double underscore, I get a model validation error.
`arecord.arecord: "unique_together" refers to common__NAIC_number, a field that doesn't exist. Check your syntax.`
This seems like it should be possible and, a slightly different question was asked that indicates it is , but perhaps I'm missing something obvious?
As Manoj implies, you can't do this with unique_together, because that is a database constraint and the sort of thing you want can't be done with database constraints.
Instead, you want do this programmatically, probably via model validation, which will ensure that no instances are created that violate your constraint.
This doesn't make sense to me. The documentation defines unique_together thus:
This is a list of lists of fields that must be unique when considered together. It's used in the Django admin and is enforced at the database level (i.e., the appropriate UNIQUE statements are included in the CREATE TABLE statement).
(Emphasis added)
I don't know how an UNIQUE statement can be added at the database level for such a case (using one column in the current table and another in a different table accessed through a foreign key). I hope those who know better about databases will correct me if I am wrong.