In the middle of the project, I was faced with the fact that I needed to expand my user model. Since it is very problematic to create a custom model at the moment, I decided to use the onetoone field and everything is successful, but there is a question. I would like to add a relationship to my user model proxy for existing users and set permissions for them. How can I do that? I need to set start value for all users
class UserPermission(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
start = models.BooleanField(default=True)
professional = models.BooleanField()
team = models.BooleanField()
last_update = models.DateTimeField()
If you can do this from your shell, then follow only step 2. If you want to do it from a migrations file, follow steps 1-3. (From the shell is easier).
In your terminal: run python manage.py make migrations app_name --empty
Add a function in your new migrations file that does the work you want to do for existing users.
def add_perms(apps, schema_editor):
UserPermissions.objects.bulk_create([UserPermissions(user_id=user_id) for user_id in User.objects.values_list("id", flat=True)], ignore_conflicts=True)
If you're doing this from your shell, you don't need the function itself. Just what it runs. UserPermissions(user_id=user_id) is just a base and you should add the defaults alongside that user_id part.
In the operations list, write:
operations = [
migrations.RunPython(add_perms)
]
Related
from django.db import models
from datetime import datetime
from django.contrib.auth import get_user_model
User = get_user_model()
class Blog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
headline = models.CharField(max_length=250)
content = models.CharField(max_length=2050)
time_created = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.user.username
every time I migrate this
"(venv) PS C:\Users\user\Desktop\APPS\web_app_project> python manage.py makemigrations"
I always get this message:
"It is impossible to add a non-nullable field 'user' to blog without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
Provide a one-off default now (will be set on all existing rows with a null value for this column)
Quit and manually define a default value in models.py.
Select an option:"
How do I go about this
Because you've added the non-nullable field user to Blog Django needs to add a user to all instances of blogs in the database, both new ones and existing. If you've created a blog instance in the database, what should Django do with its new user column? That's what it is asking you.
Only if there is no data in the database or you are completely OK with losing data, you can migrate your app to zero with python manage.py migrate <your app name> zero (You might want to reverse to migration besides zero. You can read more about reverse migrations). This will effectively undo all of your migrations for that app. You can then delete the existing migrations for that app and run makemigrations again. Django will no longer complain about the non-nullable field user, as this results in a migration that creates a Blog table with a user field, instead of a migration that attempts to add a user field to an existing Blog table. Once again, do not do this unless you are OK with losing data. This should never be done if your app is already running in production, but it is OK if you have never deployed the app and have no "real" data, and you are still in the initial development phase. Also, make sure you have a backup of deleted migrations in case you need to add them back.
As others have suggested, you can create a default user model that is used as the one-time default to add users to Blogs. For example (in Django shell)
from django.contrib.auth import get_user_model
User = get_user_model()
user = User(username='default_blog_user')
user.set_unusable_password() # Nobody should be able to log in as this user
user.save()
print(user.id) # keep this ID
Then, in the migration, you can use whatever that user.id value was as the one-time-default. But this once again assumes that you haven't deployed to production, as the one-time-default and the IDs in development and production may not match.
If you have already deployed to production, I think the only thing you can do is make the user field nullable for the sake of your migration, but assert that it is not null in your programming logic. For example, by adding a validator to the field.
Side note: instead of running get_user_model in your models module, you should do this:
from django.conf import settings
class Blog(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
# etc.
When you define a foreign key or many-to-many relations to the user model, you should specify the custom model using the AUTH_USER_MODEL setting.
source
You could probably get away with adding the user manually by using the python shell• python manage.py shell then import the required models.
Read more from a similar question here:
How to use the python shell
https://stackoverflow.com/a/5150535/15383032
Maybe add a UUID to your User Model and make the fields that require a user in other models a CharField that stores the Users UUID.
I'm tasked with upgrading the Django version for a project that currently uses Django 2.2.24.
It contains a model (with existing migrations) that looks roughly like this:
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
type = models.ForeignKey(MembershipType, on_delete=None)
Starting with Django 3.0, on_delete=None causes an error since on_delete is supposed to be a callable. In order to avoid the error, both the model and the existing migrations have to be changed.
By itself, it's not an issue to change the model like this:
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
type = models.ForeignKey(MembershipType, on_delete=models.SET_NULL, null=True)
But existing databases are not yet aware that the corresponding field can be nullable, so a new migration is required for that.
The best way I currently see to do this is the following:
change the model
create&apply a migration using Django 2.2.24
change the old migrations manually
Is there a more elegant way to solve this issue?
I'm not sure this is the optimal solution, but maybe it will help you find a similar solution at least.
If you can reset the database, then you can find the migration file where the field was first created and change on_delete to SET_NULL and set null=True. Then remove the database and run migrations from scratch.
If you can't remove the database, then you could:
Change the model as your code.
Edit migration file where the field was created. (Same as above).
Manually in the database run the SQL to alter the field to make it nullable.
I created a model for posts in Django and I'm trying to update one of the fields in the database (MySQL).
I have the following block of code:
model.py
class Post (models.Model):
title = models.CharField()
preamble = models.CharField()
body = models.TextField ()
createdAt = models.DateTimeField(auto_now_add=True, blank=True)
I want to add another field called author author=models.ForeignKey(User, on_delete=models.CASCADE
Also, I want to change the name of one of the fields from preamble to introduction
Whenever I run the command python manage.py makemigrations and then python manage.py migrate, I get an error which terminates the migration process. it's telling me that the field preamble which I have in my database is not recognized. Is it possible to update preamble to introduction?
Ensure you have a migration of the initial state of your app, before any modifications.
Rename the preamble field to introduction in your model, making no other changes.
Run makemigrations. It should ask you whether it was a rename.
Add the author foreign key.
Run makemigrations. It should ask you for a default for the user field if it's not nullable; 1 should hopefully refer to the first user created in your system.
Run migrate.
This should be work:
delete the SQLite database and also migrations directory
change the model
then run the migrate command
I want to extend my user model to hold a profile picture.
I am aware that you can create a new model and link it with foreign keys but since every type of user in my application has a Picture i figured i could add it to the user itself.
I extended the user in models.py:
class ExtendedUser(AbstractUser):
user_picture = models.ImageField(upload_to=housemate_image_path, null=True, blank=True)
Then i registered the model with the admin:
admin.site.register(ExtendedUser, UserAdmin)
And set my settings.py to:
AUTH_USER_MODEL = 'houses.ExtendedUser'
When i migrate i get:
houses.extendeduser (referred to by fields: account.EmailAddress.user, admin.LogEntry.user)
I assume it has something to with circular dependencies mentioned here and Django is proposing to create two migrations for this, but I don't understand how to go about it, can somebody help me out with this?
I want to create multiple users in django. I want to know which method will be the best..
class Teachers(models.Model):
user = models.ForeignKey(User)
is_teacher = models.BooleanField(default=True)
.......
or should I use..
class Teacher(User):
is_teacher = models.BooleanField(default=True)
.......
or I have to make custom user model...
Which will be good on creating multiple type users...??
Django doesn't have multiple users - it only has one user and then based on permissions users can do different things.
So, to start off with - there is only one user type in django. If you use the default authentication framework, the model for this user is called User, from django.contrib.auth.models.
If you want to customize user behavior in django, there are three things you can do:
Customize how you authenticate them. By default, authentication is done using a database where passwords are stored. You can authenticate against facebook/google etc. or against your existing user database - for example, with ActiveDirectory if you are on a Windows network.
Create custom permissions, and based on these permissions, restrict what functions users can execute. By default, on every model - django will add basic permissions "can edit", "can delete", "can read". You can create your own and then check if the user has these specific permissions.
You can store extra information about the user, along with whatever normally is stored by django. There are two ways to do this, depending on how much customization you need. If everything django provides by default works for you, and all you want to do is store extra information about the user you can extend the user model - in previous versions this was called creating a custom profile. The other option you have is to create your own User model, if you want deeper customization. The most common use of a custom user model is if you want to use an email address as the username.
You don't have to do all three, in fact sometimes all you want to do is store some extra information or have them authenticate using their email address; in some applications you have to modify all three places.
In your case, since all you want to do is store extra information about a user, you would need to extend the user model, by creating a model that references User (note: you don't inherit from User):
class Profile(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=200, default='Computer Science')
is_teacher = models.BooleanField(default=False)
is_student = models.BooleanField(default=True)
# .. etc. etc.
One approach I was following with Django 1.7 (works with 1.6 too) is to subclass AbstractUser
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
balance = models.DecimalField(default=0.0, decimal_places=2, max_digits=5)
To use your model you need to set it to be the one used for authentication in settings.py:
AUTH_USER_MODEL = 'your_app.User'
Also note that you will now have to use settings.AUTH_USER_MODEL when referencing
your new User model in a relation in your models.
from django.db import models
from django.conf import settings
class Transaction(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL) # ForeignKey(User) will not work