Modifying User instances in Django already in database - python

Let's say I already have existing User instances in my database. Then, I just introduced a new model in my app:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
nickname = models.CharField(max_length=30, blank=True)
I want to create a UserProfile instance for every user. I know that signals can handle this upon something like User().save(). However, what do I do with the users already in my database?
Currently I handle it in views.py:
try:
user.profile.nickname = 'my_nickname'
except:
profile = UserProfile()
profile.user = user
profile.nickname = 'my_nickname'
profile.save()
But this makes the view quite long. Is there a better way to do it?

For users already in your database, you can run a script on your django shell.
python manage.py shell
Then:
>>from .models import *
>>from django.contrib.auth.models import User
>>users_without_profile = User.objects.filter(profile__isnull=True)
>>for user in users_without_profile:
....user.profile.nickname = 'your_choice_of_nickname'
....user.save()
Just a side note: doing a wild import like from .models import * is a bad practice, but I did it anyway just for illustration and also I didn't know you appname. Hence, import the appropriate models from the respective app.
Hope this helps you.

Related

Save the username of the loggedin user in the database instead of the userid

I would like to save the username of the loggedin user in the database.
But only the userid shows up in the databse, and i can't seem to work out how to change it to the username.
Can you help me get this ?
Thanks
Here is my code
models.py
from django.db import models as db_models
from django.contrib.auth.models import User
from django.contrib.gis.db import models
class Fertidb(models.Model):
user = db_models.ForeignKey(User, on_delete=models.CASCADE)
culture = models.CharField(max_length=50)
area = models.IntegerField()
plot = models.FileField(upload_to='KML_FILES', blank=True)
def __str__(self):
return f' Parcelles de {self.user.username}'
Forms.py
from django import forms
from django.contrib.auth.models import User
from .models import Fertidb
class FertidbForm(forms.ModelForm):
class Meta:
model = Fertidb
labels = {
"plot": "Importez votre fichier KML"
}
fields = ['culture', 'area', 'plot']
views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import FertidbForm
from django.contrib.auth.models import User
title = 'FERTISAT'
#login_required
def fertisatmap(request):
if request.method == "POST":
o_form = FertidbForm(request.POST, request.FILES)
if o_form.is_valid():
fertidb = o_form.save(commit=False)
fertidb.user = request.user # o_form.user = request.user.username()
fertidb.save()
messages.success(request, f'Vos informations ont été envoyées')
return redirect('fertisat-map')
else:
o_form = FertidbForm()
context = {'title': title, 'o_form': o_form}
return render(request, 'fertisat/fertisatmap.html ', context)
You DO NOT want to store the username in your `Fertidb̀ model. Well, to be more exact, you DEFINITLY DO NOT want to store the username instead of the user id, and the chances you really want to store the username in addition of the user id are rather low.
Explication: User.username is an editable field, so it's not a viable primary key. If you try use it as ForeignKey, your data will get corrupted as soon as the user edits their username. OTHO, User.id IS a proper stable primary key, so that's what you want as foreign key. From then, you can get the username quite easily (with self.user.username), AND you will always get the up-to-date value.
The only reason you'd want to duplicate the username in your model would be to keep the user's username value at the moment the fertidb record was created. This kind of denormalization makes sense in some contexts - for example in a ecommerce app to store a product's price at the moment the user orders it (so the order's detail and total don't change when you later change your product's price...), but I fail to see how you would have such a need for a username (not that there's no valid use case here - just none I can think of xD).
IOW: keep your foreignky on User.id - that IS the RightThing(tm) -, and if you have a valid reason / requirement for duplicating the username, then do it in a distinct field.
Also, given your question, I kindly suggest that you stop writing code for a few days, learn about proper relational database modeling, and how this translates in Django ORM. Well, proper relational database modeling takes more than a few days to learn actually but at least you need to get the basics right.
Register the new model if you didnt yet in admin.py:
from django.contrib import admin
from .models import Fertidb
admin.site.register(Fertidb)
Then run python manage.py makemigrations and then python manage.py migrate.

How to clear cache from django database in version 2.1?

I am facing weird issue, after wasting so much time I found out that this is the problem because of Database cache.
I made a Model Name "Profile" later flushdb / deleted it.
After few hours I made it again but getting this error
"No such column"
then I removed all sql3db files *.pyc file etc,
& run my model again (so django will recreate db structure {also performed migrate & makemigrations}).
But still same error,
then I just renamed my db & the same code working now fine.
My problem is I have to use that old Old Model name again
but not able to get because of db cache (or maybe something)
please guide me.
admin.py
from django.contrib import admin
from django.contrib.auth.models import User
from .models import Profiles
admin.site.register(Profiles)
my model:
from django.contrib.auth.models import User
from django.db import models
class Profiles(models.Model):
# p2= models.CharField(max_length=14)
phone = models.CharField(max_length=14)
city = models.CharField(max_length=20)
province = models.CharField(max_length=20)
username2 = models.ForeignKey(User, on_delete=models.CASCADE)
Note: I have tried all old solution available on this web,
but in my case not working.
Please check this django documentation link,it will surly helps you.
https://docs.djangoproject.com/en/2.1/topics/cache/
here are the basic related queries,it may helpful for understanding.
# You can delete keys explicitly with delete(). This is an easy way of clearing the
#cache for a particular object:
#cache.delete(key, version=None)
#cache.delete_many(['a', 'b', 'c'])
#cache.clear()

In Django, is it possible for superusers to have different required fields than non-superusers?

I know that superusers and regular users are both just django's User objects, but how can I write a custom user class that requires some fields for plain users and doesn't require those fields for superusers?
No structure in the database is tricky. JSONFields for example may prove to be extremely hard to tame when the app grows.
I would go and try to make it "simple" - more maintainable (I imagine if you need to do stuff like that you may want to extend the model in the future). If this is a new project you can easily change the default user model. But that may or may not help you with your case.
You can always make two models:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class Mortal(AbstractBaseUser):
is_superuser = False
username = models.CharField(max_length=256)
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256)
class Admin(AbstractBaseUser):
is_superuser = True
username = models.CharField(max_length=256)
and then make your own authentication backend:
class MyBackend(object):
"""
Danger! A backend to authenticate only via username
"""
def authenticate(self, username=None):
try:
return Mortal.objects.get(username=username)
except Mortal.DoesNotExist:
try:
return Admin.objects.get(username=username)
except Admin.DoesNotExist:
return None
You can have a profile class (say UserProfile) with foreign key to the user that is to be created only when user signs up using the website's registration form. That way, superuser which is created on admin site or through command line wouldn't need an extra profile instance attached to it.

Django 1.7 multisite User model

I want to serve a Django application that serves multiple web sites by single database but different user sets. Think like a blog application, it will be used by several domains with different themes, but use same database by adding a site field to models.
I use Django's SitesFramework for that job. But the problem is, I couldn't separate user models for different sites. I want to use same user model with a site field and email field that unique per site.
I tried to extend AbstractUser model like that:
from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Member(AbstractUser):
username = None
site = models.ForeignKey(Site)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
on_site = CurrentSiteManager()
class Meta:
unique_together = ('site', 'email')
But gives that error: 'Member.email' must be unique because it is named as the 'USERNAME_FIELD'.
What is the best practice for that issue?
I hope this approach helps to you:
1) Compose username before save:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.sites.models import Site
from django.contrib.sites.managers import CurrentSiteManager
class Member(AbstractUser):
site = models.ForeignKey(Site)
on_site = CurrentSiteManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
class Meta:
unique_together = ('site', 'email')
from django.db.models.signals import pre_save
from django.dispatch import receiver
#receiver(pre_save, sender=Member)
def compose_username(sender, instance, **kwargs):
instance.username = "{0}__{1}".format( instance.email, instance.site_id )
2) Then overwrite ModelBackend in your custom auth backend:
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
class MyModelBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
site = kwargs.get('site')
identifier = "{0}__{1}".format( username, site )
try:
user = UserModel.objects.get(username=identifier)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
3) Remember set your custom backend on settings:
AUTH_USER_MODEL='s1.Member'
SITE_ID = 1
AUTHENTICATION_BACKENDS = ( 'MyApp.MyModule.MyModelBackend',)
4) Include site when authenticate:
>>> from s1.models import Member as M
>>> m1 = M()
>>> m1.site_id = 1
>>> m1.email = 'peter#hello.com'
>>> m1.save()
>>> m1.set_password('hi')
>>> m1.save()
>>>
>>> from django.contrib.auth import authenticate, login
>>> u=authenticate(username='peter#hello.com', password='hi', site=1)
>>> u
<Member: peter#hello.com_at_1>
>>>
Well if you want to keep the email as the USERNAME_FIELD, which by definition in the User-model must be always unique, you won't be able to repeat it for each site.
There are more than one approaches I can think of that would probably work, but I guess I would do the following:
First of all, I wouldn't extend the AbstractUser-model and make a
OneToOne dependency to the Site. Because a User is actually allowed to belong
to more than one site. So here, the best option imo is to create a
Member-model with a ForeignKey to User and a Site field and make
those unique_together. So there is only one Member per Site, and a User remains unique. Which is what represents the case better in real life too.
Now, when registering a new user for a site, just make a check first if the User (email-Address) already exists, and if so, just assign a new Member to that User. If not, create a new User as well.
First Edit to the question, "what if a User want's to register to another site with different username, password or email?"
If according to my comments, it's OK to share a user account for the sites (and of course the user is aware of this) In the register-process, in the case the user already exists for a given email, then he could be informed that, as an account for that address already exists for the site-a, this user account would be assigned to the membership to site-b. Then, an e-mail with a verify link could be sent, and when confirmed, the new member would be created and assigned to the valid user.
Another approach
If I was wrong assuming, it's ok and even desired to share users among sites, then I guess a whole new approach is needed here:
Extend the AbstractUser as you were doing, but instead of using the email as USERNAME_FIELD, use a new field composed from <email>_<site_id> (which would always be unique, as these 2 fields are unique_together)... the field could be called unique_site_id or so. And this field could be populated after submitting the sign-in and login forms.

How to extend the django User model?

I'm trying to implement the Users class from django.contrib.auth.models like this:
from django.db import models
from django.contrib.auth.models import User
class Registration(models.Model):
'''Represents a user registration.'''
user = models.ForeignKey(User)
registration_date = models.DateTimeField(auto_now_add=True, help_text='The date of the registration')
def __str__(self):
return '%s - %s' % (self.user, self.registration_date,)
This user have two attributes enabled by default: username, password
Reading at the code I can see that there are more attributes, like name and email.
How can I enable those hidden (if this is correct) attributes?
First, these attributes are not hidden. Assuming you have "django.contrib.auth" and "django.contrib.contenttypes" in your INSTALLED_APPS, then you have access to the User model as it is defined in your link. See here for the documentation on how to use/access it.
However, since you specified extending the User model, I expect you wanted to add some of your own fields to it (even though your example registration_date exists and is accessible via myuser.date_joined).
The older, more stable and more common way of doing this is similar to what you have. The only difference is to use a OneToOneField(User) instead of a ForeignKey(User). This makes the relationship bidirectional and more convenient by forcing one. You do need to make sure and create a Registration object for every User created.
In fact, there is an example of exactly what you want in the docs for the OneToOneField.
from django.db import models
from django.contrib.auth.models import User
class Registration(models.Model):
user = models.OneToOneField(User)
registration_date = models.DateTimeField(auto_now_add=True)
>>> user = User.objects.get(pk=1)
>>> registration = Registration.objects.create(user=user)
>>> user.registration.registration_date
# Should give the current time
>>> user.get_full_name()
# Should also have all those *hidden* attributes linked above
As of Django 1.5, you can use your own custom User model fairly easily. The documentation for this feature is here. If you are just adding some extra fields, then you can subclass the User model and add your own fields.
from django.db import models
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
# We inherit all those nifty things from AbstractUser
registration_date = models.DateTimeField(auto_now_add=True)
Then enable it in your settings.py by adding AUTH_USER_MODEL = 'myapp.MyUser'. We now have to access the user model a little differently
>>> from django.contrib.auth import get_user_model()
>>> Users = get_user_model()
>>> user = Users.objects.get(pk=1)
>>> user.registration_date
# Should give the current time
>>> user.get_full_name()
# Should have those 'hidden' attributes
All this is available under extending and substituting the User model in the documentation.

Categories