Set alternative pk in Django - python

I would like to make Django generate 6+ digits number id's for one Model. I don't want to start from zero but I want this id's to be clear and readable by users. So the good one is for example: 658975
How to do that?
I've tried this:
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
But uuid generates huge sequences which is not user-friendly.
Do you have any advices? Maybe setting minimum number of autoincrement pk would be enough.

Can't you simply use pk+random number to show to the user and keeping the rest of the logic same ?
Else here you go How to make a primary key start from 1000?
Write a one of migration for this.

The best options to set the primary key to start from 100000. For example(MySQL),
ALTER TABLE django_app_table_name AUTO_INCREMENT = 100000
Since these commands are depends on the database vendor, it is better to write a our own custom migration using the Django migration documentation.

Related

Can id field in django models be the same with two app instances running?

I don't understand well, how does django's autofield work... If I will run two app instances using gunicorn, will it be possible that my models get same autofield id?
I have a model Message and I want to check it's instance's id, but I want to be absolutly sure, that the ids are unique and are going by adding order.
The ids are unique for the specific model regardless of if it's within the same app or difference app. The id fields are sequential and increments by 1. Even if you delete an object, Django will not replace that ID.
There is no need to add the ID field when creating the model as Django takes care of that by itself.
If you want the id to be a unique set of character (for example- instead of the first object id being 1, you want it to be a unique number such as 12345678-1234-5678-1234-567812345678), you can use UUID (Universally Unique Identifier). In that case add the following field within your Message model-
Before running the below you would need to remove all migrations of the Message app and older records which still uses the id field.
class Message(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

many to many table in django without using manytomanyfield

rather than using the models.ManyToMany field in django
i just set up a intermediary field with a bunch of foreign keys.
is there any reason why this wouldn't work. I can't think of any but why not see if any of you have tried the same.
class Authorization(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
permission = models.ForeignKey( 'venueadmin.Permissions', blank=True, null=True)
#venue = models.ForeignKey(venue) <-- commented out cause I haven't made the model its referencing yet.
That is exactly how ManyToMany relationships work, except few things to consider.
First, you might want to check how Django generates ManyToManyField here. It does almost the same you did here.
Now, before starting using separate model think about next things:
database constraint is missing. It means that there is no validation
on what has been put in to the Authorization table as example -
Duplicate rows;
there is no indexation which means that search will become slow once Authorization grows. Interesting enough I did not find it in Django, maybe nobody cares? Two columns table might not be so critical;
there is no reason to keep an authorization record for user with no permissions. What is the reason of setting permissions to NULL? Does it carry any useful information for DB administrator in future?

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.

How do I make a primary key without auto-increment?

I've been trying to create a model that has a primary key, but I don't want that primary key to auto increment.
I know I can specify the value each time, but I want the field to be required that I specify it (hopefully enforced by the database and django), and fail fast if I forget.
It seemed logical that I would be able to say auto_increment=False on my field, but that isn't supported by the field :(
Just create id field with primary_key=True explicitly in your model:
class SomeModel(models.Model):
id = models.IntegerField(primary_key=True)
That way it won't be auto-incremented, but it will still be an primary key.

Problem trying to re-use a primary key id with SQLAlchemy

I'm trying to reuse a primary key in one of my tables with SQLAlchemy and am getting foreign key constraint error.
In a nutshell:
PostgreSQL 8.4
Python 2.7
SQLAlchemy 0.7
I have 3 tables: User, Inventories and Devices. Inventories and Devices have a one-to-one relationship with User. User.id is Inventories.user_id and Devices.user_id foreign keyed.
I've got User, Devices and Inventories set up in models/ according to standard python practices.
Within interactive python I can issue the following commands no problem:
>>>newUser = User.create()
>>>newUser.device = User.create_device(<*args>)
>>>Session.add(newUser)
>>>Session.commit()
(an inventory record is automatically created in code)
Now, let's say I want to re-use User record 1 (it's the only record that will allow a method called reset in code for security and internal testing reasons)
>>>oldUser = User.retrieve(1)
>>>Session.delete(oldUser)
>>>Session.commit()
(confirm that user 1 no longer exists)
>>>newUser = User.create()
>>>newUser.device = User.create_device(<*args>)
>>>newUser.id = 1
>>>Session.add(newUser)
>>>Session.commit()
At this point I'll either get an eror that Key(id)=(<id>) is still referenced from table "devices" (or "inventories") where <id> is the newUser.id before re-assigning it to be id 1
I've looked into cascading and have tried the various options (all, save-update, etc) with no effect.
Any information pointing to where I'm going wrong would greatly be appreciated,
Thanks,
Krys
To address the error you're seeing, you could update the foreign keys on all of the Device and Inventory models associated with that User model before committing. You'll have to make sure that your User model doesn't auto-increment the id (i.e., that it isn't a PostgreSQL sequence).
For example, the SQLAlchemy model declaration should be
class User(base):
__tablename__ = 'user'
id = Column('id', Integer, primary_key=True, unique=True, nullable=False)
instead of
class User(base):
__tablename__ = 'user'
id = Column('id', Integer, Sequence('user_id_seq'), primary_key=True)
BUT, this is probably not the right way to do it! It would be a better design to use a sequence on User.id (like in the second model declaration), and add another field on the user table that indicates if the user is an admin (for the security/testing purposes you mentioned). This way you don't have to rely on magic numbers in your application (e.g., the user id) for application logic, especially security.
I ma not using SQLAlchemy, so i do not have a proper answer, but i can say that you must ask yourself what you want is really necessary?
Because,
You probably will break the data integrity, and that may couse serious problems.
You will need to break the auto-increment structure of the ID, so until then, you have to assign id's by hand or use a hand-written pre-save trigger to get a proper id.
If you have tables that have a User foreginkey that sets NOT null, thn you probably will have problem with freeing records related to a deleted user. If you do not null them, a re-used id will create a serious data-integrity problem (wrongly referanced relations)...
So first of all, you must decide if it worth it?
Since this is a problem that shouldn't be seen in production, just use SET CONSTRAINTS. You could use INITIALLY DEFERRED on your FOREIGN KEYs but I wouldn't recommend that since you're not dealing with a cyclic dependency that exists in production.

Categories

Resources