Resolving foreign key attribute inside a model's __dict__? - django

In my ModelForm's save() method I am accessing self.changed_data and I would like to do something like:
changes_moderated_dict.update(dict(
[(i, staff.profile.user.__dict__[i])
for i in changed_data
if i in staff.profile.user.__dict__]))
which updates changes_moderated_dict with staff.profile.user key/value pairs if the key is found in changed_data. changes_moderated_dict is basically used to store all the field changes made to this particular model inside the form (for later processing and serialization).
This works all fine as long as the staff.profile.user is not a foreign key relationship in which case the filed name has a _id suffix and would not match up with the self.changed_data field name which is the attribute name as it appears in the model (without _id).
Is there a way do resolve the foreign key attribute inside a model's __dict__ to it's actual name so that the above method still works?

I'm not sure what the goal of accessing user.__dict__ is, but if it is to check the model fields you can do something like this:
u = staff.profile.user
model_field_names = map(lambda f:f.name, u._meta.fields)
changes_moderated_dict.update(dict(
[(i, getattr(u, i, None))
for i in changed_data
if i in model_field_names]))

Related

How to get all id from model in django [duplicate]

I have a table called user_info. I want to get names of all the users. So the table has a field called name. So in sql I do something like
SELECT distinct(name) from user_info
But I am not able to figure out how to do the same in django. Usually if I already have certain value known, then I can do something like below.
user_info.objects.filter(name='Alex')
And then get the information for that particular user.
But in this case for the given table, I want to get all the name values using django ORM just like I do in sql.
Here is my django model
class user_info(models.Model):
name = models.CharField(max_length=255)
priority = models.CharField(max_length=1)
org = models.CharField(max_length=20)
How can I do this in django?
You can use values_list.
user_info.objects.values_list('name', flat=True).distinct()
Note, in Python classes are usually defined in InitialCaps: your model should be UserInfo.
You can use values_list() as given in Daniel's answer, which will provide you your data in a list containing the values in the field. Or you can also use, values() like this:
user_info.object.values('name')
which will return you a queryset containing a dictionary. values_list() and values() are used to select the columns in a table.
Adding on to the accepted answer, if the field is a foreign key the id values(numerical) are returned in the queryset. Hence if you are expecting other kinds of values defined in the model of which the foreign key is part then you have to modify the query like this:
`Post.objects.values_list('author__username')`
Post is a model class having author as a foreign key field which in turn has its username field:
Here, "author" field was appended with double undersocre followed by the field "name", otherwise primary key of the model will be returned in queryset. I assume this was #Carlo's doubt in accepted answer.

Django insert data to model with foreign key

I am using django models and am running into issues. I have a couple of models that I am trying to load data to, one with a foreign key to another. I will use two models as an example, but the hope is that I can write the code so that it will generalize to work for models with different names and different field names. The models look as follows:
class ProgramInfo(models.Model):
program_code = models.CharField(max_length=5, primary_key=True)
...
class StudentInfo(models.Model):
...
program_code = models.ForeignKey('ProgramInfo', on_delete=models.PROTECT)
I also have a dictionary called _model_dict with the field names of StudentInfo as the keys and the values being the values I want to put in the model. When I run
_model.objects.update_or_create(**_model_dict)
It tells me
Cannot assign "'ABCD'": "StudentInfo.program_code" must be a "ProgramInfo" instance.
Even though that value exists in the ProgramInfo table.
I have no issue inserting data to models without foreign keys using this same method.
As I understand it the value for the key in _model_dict of the foreign key field should not just be the value for a single field, but an object of the model the foreign key links to.
I tried singling out the foreign key fields in order to be able to use model_get with filter and obtain the row from the target model and put that in the _model_dict but I couldn't find out how to obtain the target model given a known ForeignKey field. Because I intend the to generalize I don't want to specify the target model in specific code, but will like to obtain it from the field that I managed to conclude was the foreign key field. I have been googling around for many hours now and can't find the attribute for the target model a of ForeignKey field.
i don't know if it will be useful, but here is the code I used for trying to create a class for the foreign key:
# check for foreign keys
_model_field_objects = [f for f in _model._meta.get_fields()]
foreign_key_fields_dict = {}
for field in _model_field_objects:
if field.__class__ is ForeignKey:
foreign_key_fields_dict[field.name] = field #here i try to obtain target model of the field
print("Foreign key fields are: " + str(foreign_key_fields_dict))
sys.stdout.flush()
for row in data:
_model_dict = {key: value for key, value in zip(titles, row) if key in _model_field_names}
# adjust foreign key to their class
for key in _model_dict:
filter_arguments = {}
if key in foreign_key_fields_dict:
filter_arguments[key] = _model_dict[key]
_model_dict[key] = foreign_key_fields_dict[key].objects.filter(**filter_arguments)
# insert to table
_model.objects.update_or_create(**_model_dict)
I am probably way over complicating this. If there is a simple way Django provides for adding to a model with foreign keys given the known value for the foreign key field (and not the full object for the model it points to) I would be glad to learn of it.
Thanks in advance for any help available!
because program_code is the primarykey in the ProgramInfo model, you can set or get using the value of that field:
for example, lets say you have a ProgramInfo instance with a primarykey that its value is 'abcd', you can create a StudentInfo using that like this:
StudentInfo.objects.create(program_code_id='abcd', ..... other fields ....)
because program_code_id is what that really is stored in the StudentInfo database.
I solved it in an non-elegant way that works for me. Other answers given might be better, but I am too scared to change my code for now as it seems to be working. Here is what I did in case it can help anyone else:
Using:
from pprint import pprint
pprint(vars(myobject))
I managed to find out that the ForeignKey type object has an attribute ForeignKey.related_model. Once I had the model I could use it as follows to obtain the row in the model that is being pointed to:
field.related_model.objects.get(pk=_model_dict[key])
where field is the field in the model that I am trying to insert data into. The result from that could be put into the _model_dict dictionary containing all the other values for fields for the model I am inserting data into, so using the following worked for inserting the data:
_model.objects.update_or_create(**_model_dict)
NOTE:
The update_or_create would sometimes crash saying there was a problem with uniqueness, which I thought was the "update_or" part was suppose to resolve. In any case adding a try/except around it solved that problem for me - though I know its a hack..

Foregin key field name is followed by _id

In a model, when a foreign key field is created then Django apparently create another field with the same field name followed by _id.
for example if I have
class Post(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE,default=None)
dated = models.DateTimeField(auto_now=True)
...
Then I will have the following fields available:
id,user,user_id,dated
I am not sure why this field (user_id) was added?
Later I wanted to override my queryset in a class view
so I was confused which one to use (user field or user_id field)
:
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id=self.request.user.id)
Or
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user=self.request.user.id)
I tried both and both worked just fine
My question is:
1) What is the purpose of creating this additional field ?
2) What is the difference between the original foreign key field (user in my case) and user_id field?
3) Will both fields user and user_id available in the database? what is the point of that?
4) Is the content of user and user_id identical in each record? if so ,then what the purpose of this additional field that was created automatically by django?
Thanks a lot
Django only creates one column in the database for the foreign key.
The difference between the field and the _id attribute it generates is that accessing the field performs a query for the full set of columns from the related table in order to construct the complete related object. If you want the full object, use the field (and probably also use select_related() in the initial query to save you from doing N+1 queries).
On the other hand, if all you need is the DB-level value of the foreign key, which is usually the primary key of the related object (and often that is what you want), the _id attribute shortcut is there for you and already has the data, because that's what was actually in the foreign key column.
In other words, suppose I have models like this:
class ModelA(models.Model):
name = models.TextField()
class ModelB(models.Model):
name = models.TextField()
a_instance = models.ForeignKey(ModelA)
If you query for a ModelB, like ModelB.objects.get(pk=12), you'll get a query like this:
SELECT id, name, a_instance_id
FROM your_app.modelb
WHERE id = 12;
Notice a_instance_id is the name of the column -- it's just a foreign key, all it stores is a pointer to the primary key of a ModelA instance. If you just need that primary key, accessing the a_instance_id attribute has it already without needing to do another query. If you access the a_instance field, though, you get to do another query:
SELECT id, name
FROM your_app.modela
WHERE id = (whatever the value of that foreign key was);

Django handling Foreign Keys of tables in VIEWS in postgres; QuerySet always empty

Is Django or maybe POSTGRESQL losing information about primary keys and foreign keys when you create a view which relates to a view which relates to a table, which has primary and foreign keys?
I have a View-A (all 3 fields are Foreign Keys) and that view gets 2 fields from a View-B. The View-B gets its fields from a table-C. table-C has primary key and foreign key.
So when i access View-A with my django model, how do i treat those fields? I know they are foreign keys, but any kind of filter results in a empty Queryset.
if i use something like
myview = viewA.objects.using(db).all() # getting all the data
myview2= viewA.objects.using(db).all()[:5] # getting 5 objects
.
class viewA(models.Model):
class Meta:
db_table = "viewA"
x = models.ForeignKey(x, primary_key=True)
y = models.ForeignKey(y)
z = models.ForeignKey(z)
The problem is that i can not filter!
response=viewA.objects.using(db).filter(y_id=1) ERROR:= FieldError
Behind all those FK, there are integer/bigint fields.
Edit:
Since this are INNER JOINS i would like to access not only the fields from ViewA, but also from ViewB. x,y,z are from type ViewB. Maybe use select_related()?
So any clues if Django or postgres lose information about keys in views which relate to other views?
See my two comments; however, to answer your specific "query".
If you go to the docs, and see this paragraph:
https://docs.djangoproject.com/en/dev/ref/models/fields/#database-representation
You will note that foreign keys, by default, have the _id appended to their names. Now, there are occasions in which you need to access the column name directly and it's a good idea to be aware of the column as a "bigger picture" sort of thing, but at as far as Model API is concerned, you should, when doing something against a foreign key, use the attribute name given in the model instance.
Edit from your comment:
If you want to drill down and filter against some field in the foreignkey object, you just do y__fieldname = somevalue
Note that fieldname must be in the y object.
If you are getting back and empty queryset, this is because nothing in that column matched the value you gave it.
To test this, create a queryset directly on the "y" object and then try to do y.objects.fitler(fieldname=somevalue)
if you still get back and empty queryset, you know that value doesn't exist. Furthermore, you can look into the database and try a raw query in pgadminIII if you have that set up.

How to get the ID of a just created record in Django?

I'm using Django 1.3 for one of my projects and I need to get the ID of a record just saved in the database.
I have something like the code below to save a record in the database:
n = MyData.objects.create(record_title=title, record_content=content)
n.save()
The ID of the record just saved auto-increments. Is there a way to get that ID and use it somewhere else in my code?
Use n.id after the save.
See "Auto-incrementing primary keys".
It would be n.pk.
To quote "Model.pk":
Regardless of whether you define a
primary key field yourself, or let
Django supply one for you, each model
will have a property called pk. It
behaves like a normal attribute on the
model, but is actually an alias for
whichever attribute is the primary key
field for the model. You can read and
set this value, just as you would for
any other attribute, and it will
update the correct field in the model.
The ID will be automatically updated in your model, so immediately after your n.save() line you can read n.id and it will be populated.
Remove save() and get pk directly:
n = MyData.objects.create(record_title=title, record_content=content)
n.pk
If someone reading this question and after check the other answers still having problems accessing the id after the creation of the object.
Be sure you don't define id as an Integer in your model. If you decide to declare it anyways, use Autofield but you don't need to, It is for free with models.Model
#No
class TestModel(models.Model):
id = models.IntegerField(primary_key=True)
something...
#Ok
class TestModel(models.Model):
id = models.AutoField(primary_key=True)
something...
#Ok
class TestModel(models.Model):
something...
if you do define id as Integer, TestModel.objects.create( or with save() will return None.
I had a similar issue with accessing the id. In Django 3.0.5, this is how I accessed the id. Using your example and variable name, see below:
instance = n.save()
# return the id
instance[0].id
The variable 'instance' above is a list. Accessing id in the methods described above returns an AttributeError ("object has no attribute 'id'") in Django 3.
This answer applies when using modelformset_factory. This is true when creating a Form class from a Django model as described in the Django docs