Django ForeignKey Instance vs Raw ID - django

Is there a way to not have to pass in a model instance for a foreign key when create a new model? Let's say I have the following models:
class Foo(models.Model):
description = models.CharField(max_length=100)
class Meta:
db_table = u'foo'
class Bar(models.Model):
info = models.CharField(max_length=100)
foo = models.ForeignKey('Foo')
class Meta:
db_table = u'bar'
The later a post request comes in to a view - I know the the id of a foo record and just want to insert a record into the bar table.
if I do:
new_bar = Bar(info="something important", foo=foo_id)
new_bar.save()
I get a ValueError saying "Cannot assign "546456487466L": "Bar.foo" just be a "Foo" instance.
So, I get it... it wants me to have an actual instance of the Foo model. I understand that I can just do a get on Foo and then pass it in. But, there seems like there must be a way to override this functionality. I have done some googling and reading the docs, and raw_id_fields in admin seems to be the basic idea. (which is to say, allow a raw id here). But, don't see this option on the ForeignKey field.
It seems very inefficient to have to make a round trip to the database to get an object to get the id (which I already have). I understand that doing the round trip validates that the id exists in the database. But, hey... that's why I'm using a RDBMS and have foreign keys in the first place.
Thanks

new_bar = Bar(info="something important", foo_id=12345)
new_bar.save()
You can also get foreign key values directly. Some kind of optimization.

Related

How to optimize call to database with OneToOneField?

According to the documentation, to optimize the db access :
If you only need a foreign key value, use the foreign key value that
is already on the object you’ve got, rather than getting the whole
related object and taking its primary key. i.e. do:
entry.blog_id
No problem to use with a ForeignKey and it works as intended.
But if I want to do the same with OneToOneField, it is not working :
Class CustomUser(Model):
...
class Profile(Model):
user = models.OneToOneField(
CustomUser,
on_delete=models.CASCADE,
)
Then, if I try to use the same tip as described in the documentation, in a view for example :
request.user.profile_id
I got the followig error :
AttributeError: 'CustomUser' object has no attribute 'profile_id'
Of course, it works with request.user.profile.uid but it is not the point here because there is an additional query to the DB.
Is it intended ? Is it a con to using OneToOneField ?
Since you're doing it in reverse (it's the model Profile that has the field user_id, and not CustomUser that has the field profile_id) I think you can't use this optimization. You'd have to move the OneToOneField to CustomerUser model. You can still access the object of course, but you're going to hit the database once more.
Edit
If possible for your project, this should work:
class Profile(Model):
...
class CustomUser(Model):
profile = models.OneToOneField(
Profile,
on_delete=models.CASCADE,
)
request.user.profile_id

How could django's save() method of model know where to save among db tables?

Save() function must know where to save data, especially the exact table.
Assume that there exist Question model and I create a new record and save it through q.save() like below.
Although I haven't give the information of table actually, but it works well.
I want to know how save() method can know the table name needed.
def create_question(request, question):
q = Question(question_text=question, pub_date=timezone.now())pub_date=timezone.now())
q.save()
It obtains this out of the _meta of the Question model. Indeed, you can obtain the name of the table with:
Question._meta.db_table
So if your application is named app, then likely the name is:
>>> Question._meta.db_table
'app_question'
Note that you can specify the name of the table yourself, by specifying a db_table attribute [Django-doc] to the Meta of the model. For example:
class Question(models.Model):
class Meta:
db_table = 'name_of_question_table'

How to reference model joined across foreign key in django admin

I have a django 1.6 app with the following (trimmed for clarity)
classes defined. User is the standard django.contrib.auth User class.
class Event(models.Model):
user = models.ForeignKey(User, related_name='events')
name = models.CharField(max_length=64)
class Profile(models.Model):
user = models.ForeignKey(User, related_name='aprofile')
class MemberProfile(Profile):
pass
Here are my admin classes:
class ProfileAdmin(ModelAdmin):
model = Profile
fields = ('user', )
class MemberProfileAdmin(ModelAdmin):
model = MemberProfile
fields = ('user', )
readonly_fields = ('user', )
What I'd like to do is display a read-only list of all events for a given member, or at least profile. Of course joining across the User foreign key seems like the way to go, but I am drawing a blank as to how to accomplish this. Here's a summary of attempts so far.
Define an inline admin on the Event class directly referencing the user field, and add it to the ProfileAdmin:
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user' # Fails - fk_name 'user' is not a ForeignKey to <class 'solo365.solo_profile.models.profile.Profile'>
...well, no, it sure isn't. But our User has an 'aprofile' field, so...
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user__aprofile' # Fails - EventInlineAdmin.fk_name' refers to field 'user__aprofile' that is missing from model 'admin_fk_test.Event'.
Ok, those fields look like they should sync up, but perhaps we need to be a little more aggressive:
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user__aprofile__pk' # Fails - 'EventInlineAdmin.fk_name' refers to field 'user__aprofile__pk' that is missing from model 'admin_fk_test.Event'.
I've also tried messing with formfield_for_foreignkey() and friends in both the inline and the regular model admins, but without that fk_name having a valid value, those methods never get called.
I then considered trying to access the events field directly from a Profile's user:
class ProfileAdmin(ModelAdmin):
model = Profile
fields = ('user', 'user__events') # Fails - Unknown field(s) (user__events) specified for Profile. Check fields/fieldsets/exclude attributes of class ProfileAdmin.
What about with a custom formfield_for_foreignkey() method? Sadly that never gets called for anything other than the 'user' field. I've also considered a custom get_formsets() method, but frankly I'm not sure how I could use that without a working EventInlineAdmin.
I could of course define a custom field that simply concatenates all of the events and returns that as a string, but ideally I would prefer something like a fully-featured inline (even read-only) than just a chunk o' text. IOW such a custom field would have a method that (ideally) would return an inline form without requiring any sort of custom template, setting of allow_tags, etc.
Am I doomed to have to create a completely custom Form for the Profile admin class? Or is there a simple way to accomplish what I'm trying to do, that I'm just missing?
Update:
Bonus points if a provided solution works for the MemberProfileAdmin class, not just the ProfileAdmin class.
The relation between User and Profile should be a 1:1 relation which would allow the referencing via user__aprofile. Otherwise, the reverse relation of a foreing key is a queryset because one foreign key can be assigned to multiple instances. This is might be the reason why your code failed.
Change it to:
class Profile(models.Model):
user = models.OneToOneKey(User, related_name='aprofile')
This is a bit like using ForeignKey(unique=True).
To know the attributes, it might help to call dir(model_instance) on the model instance in question, or try around in the Django shell (./manage.py shell).
Also, I've experienced that it might be more confusing to assign a custom related_name like in your case where you would expect one profile by looking at the related name but you would actually get back a queryset.
The generated name in that case would be profile_set, and you would have to call profile_set.all() or profile_set.values() to get some actual profiles.

How to display ManyToManyField field values from a ForeignKey in Django template ?

I looked in the QuestSet API and tried to find the answer. But i guess i am misinterpreting the distinction between a Manager and an instance.
I have the following model...
class Target(model.Model):
group = models.ManyToManyField(Group, null=True)
group_name = models.ManyToManyField(GroupName, null=True)
...
class Group(models.Model):
value = models.CharField(null=True)
And so on is defined for all the ManyToMany relations in the Target Model.
Now, i have a reference to the Target Model itself from another model as follows.
class Schedule(models.Model):
targetID = models.ForeignKey(Target, null=True)
name = models.CharField(null=True)
In my template for the Schedule model, I want to be able to display the values of the ManyToManyFields which are referenced in the ForeignKey (i.e the Target).
When i write the following,
o = Schedule.objects.get(name = 'O_123')
o.targetID ---> This gives me the ID of the ForeignKey field
I want to be able to display the value of all the field which are there in the Target Model with reference to the name(i.e. O_123), For this i tried the following,
o.targetID.group
this displays "django.db.models.fields.related.ManyRelatedManager object at 0x1f2e850"
Can someone help me understand what am I misinterpreting. Thanks.
You have some misconceptions.
o.targetID ---> This gives me the ID of the ForeignKey field
No. o.targetID gives you the actual Target object that is related via your ForeignKey. That's why you should not call the field anythingID: it should just be called target.
Now, this is possible because that is the many-to-one side of the FK relationship: ie, for each Schedule, there is only one Target. But from Target to Group there is a many-to-many relationship. So o.targetID.group refers to many groups (again, naming conventions help: you should probably call this field groups).
So Django gives you a Manager object: basically, just the same as when you do Schedule.objects. Just as with that latter case, you can do all() to get all of the elements to iterate over, or get(criteria=value) to get a specific one, or even filter(criteria=value) to get a filtered queryset.
In your case, you probably want:
for group in o.targetID.group.all():
print group.my_field_value

How can I get the ForeignKey field instead of the related object in Django?

I have this:
Class A(models.Model):
name = models.CharField(max_length=50)
Class B(models.Model):
a = models.ForeignKey(A)
class C(models.Model):
a = models.ManyToManyField(A)
When i need the attribute a in an object of C:
related_manager = getattr(object_c,'a')
and this give me a ManyRelatedManager but the problem is when i need the attribute a in a object of B:
object_b2 = getattr(object_b,'a')
this give me a object of class B and i need know if is a ForeignKey or ManyToManyField, i mean, i need getattr(something, 'some_attribute') and get the models.* not the object in self.
I've run into this before with getattr. Model introspection is the solution.
If you know the field name, the best way to do it is to use a method in the object's _meta section (ooooh, scary!).
object_b2._meta.get_field_by_name('a')[0]
That [0] at the end is because the method actually returns lots of other useful and interesting information in a tuple. But if you just want the field itself, that's the way to do it.
Without giving code, the way to do it if you don't know the name of the field is to iterate of _meta.fields and find the one where field.rel.to is the model you're looking for.
Good luck!
A ForeignKey only leads to a single model. If you need to get a manager when accessing the attribute then use ManyToMany with an intermediary table that has a ForeignKey with unique enabled.