ManyToManyField references the 'id' field when using a custom primary key - python

I am using a custom primary key for a model that has a few ManyToManyFields. When I update the model and add an object to a ManyToManyField (using add(new_object)), I get an error signifying that its looking up the primary key using the id field (which perhaps exists in the intermediary table, but not in the model).
psycopg2.DataError: invalid input syntax for integer: "TL98GK"
LINE 1: ...WHERE ("placedir_place_place_categ"."place_id" = 'TL98GK' A...
I have been searching on SO for a while but havent been able to zero in the exact issue. I guess I may have to use custom through table for ManytoManyFields (as a punishment for using custom primary key) but I honestly dont want to go down that route.
Using Django 1.10 and Python 3

It seems the migration doesn't detect Foreign Key type changes well. It's a known bug. A workaround is mentioned here (i.e to explicitly change the field type to varchar(32)).
However, you may need to do more (like updating the constraints etc. on the table) depending on your use case. (For those interested to go that route, here is one example case with corresponding migration code).
(p.s I just decided to not use a custom primary key on models with manytomany fields)

Related

Django Admin actions don't work after redefining primary key of the model

I am using standard models.Model in Django. I changed my primary key to _id (mongo). After that, actions in Django Admin don't work anymore. For example, when I use the default Django action – Delete selected, no object is found through the query. However, no problem occurs when I edit or delete objects manually. Is there any way to fix it?
Are you sure you want to delete the selected companies? -> Summary, objects: None

Django TextField default value

In one of my tables I have a field game_fen_when_leave = models.TextField(). But it gives me an error "You are trying to add a non-nullable field 'game_fen_when_leave' to game without a default; we can't do that (the database needs something to populate existing rows)". Is it necessary for this field to have a default value? I saw an example without having a default.
Short answer
When creating a new model: No it is not
When adding it to an existing model: Yes it is
A bit more on the topic:
With the information given I guess your are about to add this new field to an existing table.
When adding a new non-nullable fields to an existing model you will need to provide a default value. This is because there might already be rows in that particular table and those would need a default value to populate this new field with. (I'm actually just repeating the error message here.)
In the example that you are referring:
The model is new and there cannot be existing rows that would need to be populated with default values. Therefore default value for the TextField is not needed.
Couple of possibilities
Remove and create the model from scratch: If you remove the table by migrations and create it again as a completely new table. You don't have to provide a default value as there cannot be existing rows.
Add a default value: Default value could simply be an empty string and that probably is the way to go.
By default Django TextField is a non-nullable yes. You have the power to change that, but it is not advised to do so:
https://docs.djangoproject.com/en/3.0/ref/models/fields/#null
If a string-based field has null=True, that means it has two possible
values for “no data”: NULL, and the empty string.

Why does Django set a shorter maximum length of foreign keys to auth.User.username?

I have a model with a foreign key that references the username field of auth.User. The original field has a maximum length of 150. But Django generates a foreign key with a maximum length of 30.
In my app's models.py:
class Profile(models.Model):
user = models.ForeignKey('auth.User', to_field='username')
In django.contrib.auth.models:
username = models.CharField(
_('username'),
max_length=150,
Generated SQL:
CREATE TABLE "myapp_profile" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"user_id" varchar(30) NOT NULL REFERENCES "auth_user" ("username")
);
This only happens when referencing auth.User.username. If I reference a long field in my own model, the foreign key is generated fine.
Why is that? How can I overcome it?
Using Django 1.11.4 and Python 3.6.2. I tried PostgreSQL and SQLite and the problem occurs on both.
CLARIFICATION:
From the answers so far I think my question was misunderstood. I am not looking for a way to have long usernames. My problem is that the stock User model that comes with Django has one max_length (150), but when your model refers to it, the foreign hey has a shorter max_length of 30. Therefore if a user is registered with a username of 31 characters, I will not be able to create child objects of that user, because the foreign key constraint will be violated. And I need this because I have a REST API whose URLs nest resources under uses, that are referred by username, not ID. For example: /users/<username>/profiles/...
UPDATE:
I think the reason for this behavior is the undocumented swappable property of the User model. It is designed to be replaceable by custom models. However, the configured model must have its data in the initial migration of the app that defines the model. The migrations code seems to generate references to the initial migration of swappable models. I am using the default User model, and its initial migration sets the username to 30 chars. Hence my username FKs are 30 chars long. I am able to work around this with a RunSQL migration to alter the FK data type to varchar(15), but I am in doubt if it's the right thing to do.
Is recommended use short identifier, varchar(30) is a long number, something like 999999999999999999999999999999, when Django make identifiers always use the same number. I don't think that you are going to use so much users if you reach that number you should create another type of identifier. Remember the long of the user_id field is the id of the username and not the string
You can use this hack described in this SO answer,
but be very careful!.
Or you can use this package.
However, I think that, as described in this discussion, the best way would be to create a custom User model and do whatever you want there.
Hope it helps!
You must use custom user model.Taken from django docs.
150 characters or fewer. Usernames may contain alphanumeric, _, #, +, . and - characters.
The max_length should be sufficient for many use cases. If you need a longer length, please use a custom user model. If you use MySQL with the utf8mb4 encoding (recommended for proper Unicode support), specify at most max_length=191 because MySQL can only create unique indexes with 191 characters in that case by default.

Django - any way to fetch foreign key objects instantly?

I've got a question about foreign key behaviour in Django.
I've defined a tree hierarchy in my models, where a parent-son relation is represented as a foreign key in the son model. Now, starting at the leaf level, I'd like to retrieve the parent, the parent's parent etc. as the objects I've defined.
This is possible by simply calling Leaf.objects.all() and accessing the objects normally from Python code.
But here come the troubles. For each such call, Django makes a SELECT query for the appropriate foreign ID. This is obviously terribly slow and inefficient. I'd like to tell Django something like "hey, just fetch me all the data including the foreign keys at once, just do the joins and all the stuff at the database side". Is that somehow posible?
Just use select_related():
Leaf.objects.select_related().all()

Passing a model instance, not __unicode__ method in django

I've got a django form that contains a join via a foreign key. This is a foreign key to a very large table. On the form, to prevent loading up a massive select that tends to crash browsers, I've got a jQuery autocomplete, which on each keystroke sends off the entered text. This text is then searched in the table and suitable results are returned to be displayed. The id is then passed to a hidden CharField when one is selected. This hidden CharField is the widget for the ForeignKey relation. When I try to save the form, I get an error that I need to be passing a model instance for the related model, which is fair enough. I can't work out how to do this however. I can take that id and do a model.objects.get(pk=id_from_form), but if I replace the POST data with the result of this, I still get an error as I'm just passing the __unicode__ method of the model. I'm sure there's something I'm missing, but I can't see what it is.
Thanks.
Instead of using a CharField to store the id, try using a ModelChoiceField with the widget set as a HiddenInput. The field definition in your form would look something like:
mymodel = forms.ModelChoiceField(widget=forms.HiddenInput, queryset=MyModel.objects.all())

Categories

Resources