How to mention other field as Foreign Key in Django Model - django

In my model i have
book_id PK
book_number as unique field
Now in my Author model i want to set up
book_number as Foreign key not book_id
Is this possible

to_field.

ForeignKey refers to an entity by it's primary key, by database design. You should probably not do it although, you might be able to fake it within django by lieing within Model.Meta what the primary key is.
The right approach seems to me, is to make the book_number, the PK, if you are confident there aren't any repeats, and may be still retain the serial type for book_id.

Related

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..

How should a many-to-many table be defined

I'm having some trouble understanding many-to-many fields in Django.
When I create a many-to-many field, ex:
class GlobalPart (Models.model):
...
category_id=models.ManyToManyField(Category, related_name = 'globalpart')
...
and
class Category (Model.model):
...
category = models.CharField(max_length=250)
...
I notice that it created a new table called appname_globalpart_category_id in addition to the appname_globalpart table for the GlobalPart model.
What I'm wondering is, how should the field types in that table be defined. I would think that
there should be at least one foreign key there to relate the fields. But instead there is the primary key for the table, and the other fields are integers (globalpart_id and category_id).
So my question is -- is that normal? Or did I somehow define the many-to-many field incorrectly? And my next question is how would I get all the category_ids associated to a particular GlobalPart?
(1) short answer: Yes this is normal.
Long answer: ManyToMany table will need a foreign key to both Category and GlobalPart tables. Strictly speaking those two foreign keys should be sufficient. The extra pk that you see in there is just for django magic. You can really get away with only those two foreign keys in that table if you manually define the many-to-many table yourself. However if you let django do it for you (by using ManyToManyField) you get this extra pk
(2) I suggest changing your model fields category_id to categories:
class GlobalPart (Models.model):
categories=models.ManyToManyField(Category, related_name = 'globalpart')
This is because, ManyToManyFields refers well to "many" items. This field does not refer to "one" category_id, it refers to all related categories. So when naming it would be natural to name it accordingly.
As for accessing all categories you can do it by accessing the "categories" property. Say if your object instance named global_part, you can access categories like this:
categories = global_part.categories.all()
Instead of all(), you can use filter() or exclude() the same way you use it when querying models.
Here is a link to related django docs
What do you think a foreign key is? It's a field containing values that equate to IDs - usually primary keys - in the "foreign" table. If the other table has integer keys, as most Django tables do, then the foreign key field will be of type integer as well.
Additionally, Django creates constraints so that the database will enforce that the IDs do actually reference valid values in the foreign table. Depending on your database, these might or might not be displayed as part of the field definition.

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.

tell django not to follow foreign key?

Is there a way to tell django not to follow a foreign key relationship when you instantiate a model instance? Something to put on the model itself? Something to pass to a queryset? I'd like to have a queryset that only returns instances with the foreign key id -- I don't want the instances to go off making queries to find its relatives. Something like the opposite of select_related?
The default behaviour of Django is to wait until a foreign key relationship is accessed before performing a database queries to populate the related model instance.
To side-step the automatic querying for related instances, rather than accessing the ForeignKey field attribute directly, access attribute_id, e.g.
class Person(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey('auth.User')
# access the user id via user_id
person = Person.objects.all()[0]
print person.user_id
Try defer

Edit/show Primary Key in Django Admin

It appears Django hides fields that are flagged Primary Key from being displayed/edited in the Django admin interface.
Let's say I'd like to input data in which I may or may not want to specify a primary key. How would I go about displaying primary keys in the admin interface, and how could I make specifying it optional?
I also wanted to simply show the 'id' (primary key) within the Django admin, but not necessarily edit it. I just added it to the readonly_fields list, and it showed up fine. IE:
class StudentEnrollmentInline(admin.TabularInline):
model = Enrollment
readonly_fields=('id',)
whereas if I tried to add it to the 'fields' list, Django got upset with me, saying that field didn't exist...
If you explicitly specify the primary key field in your models (with primary_key=True), you should be able to edit it in the admin.
For Django models created via ./manage.py syncdb the following primary key field is added automatically:
id = models.AutoField(primary_key=True)
if you change (or add) that to your model explicitly as an IntegerField primary key, you'll be able to edit it directly using the admin:
id = models.IntegerField(primary_key=True)
But as others pointed out, this is a potential minefield...
To show the primary key, which by default will have a column name of "id" in the database - use "pk"
def __str__(self):
return '{} - {} ({})'.format(self.pk, self.name, self.pcode)
It doesn't make sense to have an optional primary key. Either the PK is an autoincrement, in which case there's no need to edit it, or it's manually specified, in which case it is always required.
Why do you need this?
In django documentation, there is a short sentence about that, which is not clear:
If neither fields nor fieldsets options are present, Django will default to displaying each field that isn't an AutoField and has editable=True, in a single fieldset, in the same order as the fields are defined in the model.
Reason is, django do not allow you to edit an AutoField by any means (and that is the right thing since it is an auto increment value and should not be edited). #mnelson4's answer is a good approach to display it.
The answer with the highest votes didn't work for me. I needed a getter.
class StudentEnrollmentInline(admin.TabularInline):
model = Enrollment
readonly_fields=('student_enrollment_id',)
def student_enrollment_id(self, obj):
return obj.id
Using django 1.11