Unique email constraint for django.contrib.auth.user - django

I have changed django.contrib.auth.user model where I have made the email id to be unique. After which I added a datamigration to reflect the same-
python manage.py datamigration appname unique_password.
Followed by python manage.py schemamigration appname --auto-->no changes. Finally, python manage.py migrate appname.
Migrating forwards to 0003_unique_password
appname:0037_unique_password.py
Now when I add a user whose email is not unique, it gives me an error and does not let me create the user through the django admin. But when i do:
>
python manage.py shell
from django.contrib.auth.models import User
user=User('cust1','123','xxx#gmail.com')
This creates a user object even though 'xxx#gmail.com' already exists.
How can I add an unique constraint to django.contrib.auth.user.email for an existing project (with data)?

Checks for uniqueness occur during model validation as per the Model validation documentation. Calling save() on the model doesn't automatically cause model validation. The admin application will be calling model validation.
Nothing prevents you from creating a User object in Python such as user=User('cust1','123','xxx#gmail.com') and then doing a user.save(). If there are uniqueness checks at the database level, you'll likely get an exception at this point. If you want to check before doing the save, call is_valid() on the Model instance.
If you want every User object to be automatically validated before save, use the pre_save Django signal to do Model validation before the save proceeds.

Related

How do you migrate a custom User model to a different app in Django?

In Django, I am trying to move a custom User model from one app to another. When I follow instructions such as found here, and try to apply the migration, I get errors with ValueError: Related model 'newapp.user' cannot be resolved that originate from django.contrib.admin.models.LogEntry.user, which is defined as models.ForeignKey(settings.AUTH_USER_MODEL, ...), so that is a model from the Django admin (which I also use) that has a foreign key to the User model. How can I do this migration?
For swappable models (models that can be swapped out through a value in settings) such a move is non-trivial. The root cause of the problem here is that LogEntry has a foreign key to settings.AUTH_USER_MODEL, but the history of settings.AUTH_USER_MODEL itself is not managed or known to Django migrations. When you change AUTH_USER_MODEL to point to the new User model, that retroactively changes the migration history as Django sees it. To Django it now looks like LogEntry's foreign key has always referenced the new User model in destapp. When you run the migration that creates the table for LogEntry (e.g. when re-initializing the database or running tests), Django cannot resolve the model and fails.
See also this issue and the comments there.
To work around this problem, AUTH_USER_MODEL needs to point to a model that exists in the initial migration for the new app. There are a few approaches to get this working. I'll assume the User model is moved from sourceapp to destapp.
Move the migration definition for the new User model from the last migration (where Django makemigrations would automatically put it) to the initial migration of destapp. Leave it wrapped in a SeparateDatabaseAndState state operation, because the database table is already created by the migrations in the sourceapp. Then, you'll need to add a dependency from the initial migration of destapp to the last migration of sourceapp. The problem is that if you try to apply the migrations as they are now, it will fail because destapp's initial migration has already been applied while it's dependency (sourceapp's last migration) has not been. So you will need to apply the migrations in sourceapp before adding the above migrations in destapp. In the gap between applying sourceapp and destapp's migrations the User model won't exist so your application will be temporarily broken.
Apart from temporarily breaking the application, this has the other problem that now destapp will depend on sourceapp's migrations. If you can do that, that's fine, but if there already exists a dependency from a sourceapp migration to a destapp migration this won't work and you've now created a circular dependency. If that's the case, look at the next options.
Forget about the User migration history. Just define the User class in destapp's initial migration, without the SeparateDatabaseAndState wrapper. Make sure you have CreateModel(..., options={'db_table': 'sourceapp_user'}, ...), so the database table will be created the same as it would when User lived in sourceapp. Then edit sourceapp's migration(s) where User is defined, and remove those definitions. After that, you can create a regular migration where you remove User's db_table setting so the database table gets renamed to what it should be for destapp.
This only works if there are no or minimal migrations in the migration history of sourceapp.User. Django now thinks User always lived in destapp, but its table was named as sourceapp_user. Django cannot track any database-level changes to sourceapp_user anymore since that information was removed.
If this works for you, you can either forego any dependencies between sourceapp and destapp, if sourceapp's migrations don't need User to be there, or have sourceapp's initial migration depend on destapp's initial migration so that the table for User is created before sourceapp's migrations are run.
If both don't work in your situation, another option is to add the definition for User to sourceapp's initial migration (without SeparateDatabaseAndState wrapper), but have it use a dummy table name (options={'db_table': 'destapp_dummy_user'}). Then, in the newest migration where you actually want to move User from sourceapp to destapp, do
migrations.SeparateDatabaseAndState(database_operations=[
migrations.DeleteModel(
name='User',
),
], state_operations=[
migrations.AlterModelTable('User', 'destapp_user'),
])
This will delete the dummy table in the database, and point the User model to the new table. The new migration in sourceapp should then contain
migrations.SeparateDatabaseAndState(state_operations=[
migrations.DeleteModel(
name='User',
),
], database_operations=[
migrations.AlterModelTable('User', 'destapp_user'),
])
so it is effectively the mirror image of the operation in the last destapp migration. Now only the last migration in destapp needs to depend on the last migration in sourceapp.
This approach appears to work, but it has one big disadvantage. The deletion of destapp.User's dummy database table also deletes all foreign key constraints to that table (at least on Postgres). So LogEntry now no longer has a foreign key constraint to User. The new table for User doesn't recreate those. You will have to add the missing constraints back in again manually. Either by manually updating the database or by writing a raw sql migration.
Update content type
After applying one of the three above options there's still one loose end. Django registers every model in the django_content_type table. That table contains a line for sourceapp.User. Without intervention that line will stay there as a stale row. That isn't a big problem as Django will automatically register the new destapp.User model. But it can be cleaned up by adding the following migration to rename the existing content type registration to destapp:
from django.db import migrations
# If User previously existed in sourceapp, we want to switch the content type object. If not, this will do nothing.
def change_user_type(apps, schema_editor):
ContentType = apps.get_model("contenttypes", "ContentType")
ContentType.objects.filter(app_label="sourceapp", model="user").update(
app_label="destapp"
)
class Migration(migrations.Migration):
dependencies = [
("destapp", "00xx_previous_migration_here"),
]
operations = [
# No need to do anything on reversal
migrations.RunPython(change_user_type, reverse_code=lambda a, s: None),
]
This function only works if there is no entry in django_content_type for destapp.User yet. If there is, you'll need a smarter function:
from django.db import migrations, IntegrityError
from django.db.transaction import atomic
def change_user_type(apps, schema_editor):
ContentType = apps.get_model("contenttypes", "ContentType")
ct = ContentType.objects.get(app_label="sourceapp", model="user")
with atomic():
try:
ct.app_label="destapp"
ct.save()
return
except IntegrityError:
pass
ct.delete()

wrong url to admin django after adding profile

I had a login page that I would change it to another login page and I follow these instructions.
I added this code and when I tried to login to my user admin that I added before it sends me to wrong url http://localhost:8050/admin/login/?next=/admin/ and it throws :
RelatedObjectDoesNotExist at /admin/login/
User has no profile
The profile instance is generated on post_save signal, that is, you must save your User at least once after you added that Profile class.
The easiest workaround in your case would be to create a new admin user using python manage.py createsuperuser.
The error seems like, You have a User in DB, but it doesn't have any profile relation now.
Solution:1
Set profile instance for currently existing User's in db through django shell.
1. log into django shell by python manage.py shell
2. run these commands,
from django.contrib.auth.models import User
from myapp.models import Profile
for user in User.objects.all():
Profile.objects.create(user=user)
3. then login to the django admin
Solution:2
Drop your database, (if you are using sqlite, the delete the corresponding file) and migrate and then create a new super user by python manage.py createsuperuser command

Django: how to add a field to an existing model without much rewrite of the code

our website lists local city events. It's Django-based so there's a lot of code related to 'event' model. Until now, we work only in one city and so all the events mean to be local.
Now we need to extend the website to another city. This means 'event' model gets a new attribute 'city', and our middleware will set a global value CurrentCity based on geoip.
We need to extend 'event' model so it would filter only records where 'city' attribute equals to CurrentCity value. There is too much code in different views and models working with the 'event' so we can't update each module.
Is there any single place to patch that would make our 'event' model aware of the CurrentCity value?
Depending a lot in your structure and Django version I think you have 2 options.
South
The best one is to install the application "South". I don't know if you already know it or you're using it but I think it should be your first option.
In case you're not using it, you should do this steps:
Install with pip install South
Create your first migration with:
python manage.py schemamigration YOURAPPNAME --initial
You need to fake this migration, because you have already the fullfilled database so you need to do:
python manage.py migrate YOURAPPNAME --fake
Add the new field to the model Event in the file models.py
Generate the new migration to make South create the new field in your database with:
python manage.py schemamigration YOURAPPNAME --auto
Final step, execute the migration created with:
python manage.py migrate YOURAPPNAME
Tips
--initial for the first migration --auto for the rest
The initial migration is faked because you already have tables in your database, if you try to migrate without the fake it will return error "Table already exists"
New Model City
Another option, in case you can't modify your actual Model, or maybe if it's too messy, another option is to generate an externa Model City like this:
class City(models.Model):
event_foreign = models.ForeignKey(Event)
event_many = models.ManyToManyField(Event, blank=True, null=True)
name = models.CharField....
postal_code = models.CharField....
# etc...
I don't know wich is optimal for you, a Foreign Key or a ManyToMany, depends if a City can have more than 1 Event or no, it's your choice.
When you have a model like this you can access from this City model to the Event (because of the ForeignKEy or ManyToMany) but this relation goes also in the other direction, if you have an Event you can get the City/cities related to it I'm gonna show two examples:
Example 1 using Foreign Key
city = City.objects.get(id=1)
city.event # Returns event
event = Event.objects.get(id=1)
event.city # Returns city
Example 2 using Many to Many
city = City.objects.get(id=1)
city.event.all() # Returns a list of events
event = Event.objects.get(id=1)
event.city_set.all() # Returns a list of cities

South: changing ForeingKey

i had a profile field in todo models which a ForeignKey to Profile model
Instead of using profile i want to use the user field of User model as a ForeignKey .
Steps:
I have removed the profile field from todo model, created and applied migration for that.
Works fine, profile field do not exist in database.
2, I added user field as a ForeignKey in todos models .
When doing python manage.py schemamigration todos --auto
gives response like:
Nothing seems to have changed.
what am missing here ?

Django: text fixture fails to load

Did a dumpdata of my project, then in my new test I added it to fixtures.
from django.test import TestCase
class TestGoal(TestCase):
fixtures = ['test_data.json']
def test_goal(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
When running the test I get:
Problem installing fixture
'XXX/fixtures/test_data.json':
DoesNotExist: XXX matching query does
not exist.
But manually doing loaddata works fine does not when the db is empty.
I do a dropdb, createdb a simple syncdb the try loaddata and it fails, same error.
Any clue?
Python version 2.6.5, Django 1.1.1
Perhaps you have some foreign key troubles. If you have a model that contains a foreign key referring to another model but the other model doesn't exist, you'll get this error.
This can happen for a couple of reasons: if you are pointing to a model in another app that you didn't include in the test_data.json dump, you'll have trouble.
Also, if foreign keys change, this can break serialization -- this is especially problematic with automatically created fields like permissions or generic relations. Django 1.2 supports natural keys, which are a way to serialize using the "natural" representation of a model as a foreign key rather than an ID which might change.