Django custom user with tuple as username - python

I have a solid level on Django, but can't find how to solve this one properly :
I created an API that will be used in multiple mobile projects. So I implemented an Application model that I pass when I log in.
I also created a custom user model :
class CustomUser(AbstractUser):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
username = None
email = models.EmailField('email address', unique=True)
application = models.ForeignKey(Application, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
class Meta:
unique_together = ('email', 'application')
I'm using djangorestframework-jwt. So I've an API call that needs an email, password and an Application (apikey as a string FYI) to get a token.
Everything is working fine, except that in this configuration, I'm not able to create another user with the same email but a different Application. Because my EmailField is my USERNAME_FIELD.
Is there any solution that would avoid me to rewrite everything ?
The best thing I have in mind is to add a field (for example a CharField) that would be unique (for example the combinaison of user.id + apikey) that would be filled automatically on creation. But then when I use ./manage.py createsuperuser, django would ask me to fill the field manually. Not a big deal but if you have a better/proper way I'd be glad !

How about use manytomany instead of foreignkey:
class CustomUser(AbstractUser):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
username = None
email = models.EmailField('email address', unique=True)
applications = models.ManyToManyField(Application, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
class Meta:
unique_together = (('email', 'application'),)

Related

Django problem overriding all auth Signup forms

Thanks in advance, i'm learning Django and can't figure how to override all auth forms. Quick explanation first, I have a custom user model
class PersoUser(AbstractBaseUser):
email = models.EmailField(
verbose_name="Email Adress", max_length=200, unique=True)
username = models.CharField(
verbose_name="username", max_length=200, unique=True)
first_name = models.CharField(verbose_name="firstname", max_length=200)
last_name = models.CharField(verbose_name="lastname", max_length=200)
date_of_birth = models.DateField(verbose_name="birthday")
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
objects = PersoUserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["date_of_birth", "username"]
....
and I would want to add date_of_birth field to my signup page, so I followed the official doc to override the specif form used by all auth SignupView https://django-allauth.readthedocs.io/en/latest/forms.html#signup-allauth-account-forms-signupform
which leads to ( in Book_store/forms.py )
from all auth.account.forms import SignupForm from users. models import PersoUser
class PersoUserRegisterForm(SignupForm):
class Meta:
model = PersoUser
fields = ["username", "email", "first_name",
"last_name", "date_of_birth", "password1", "password2"]
def save(self, request):
# Ensure you call the parent class's save.
# .save() returns a User object.
user = super(PersoUserRegisterForm, self).save(request)
# Add your processing here.
# You must return the original result.
return user
in my settings/base.py
ACCOUNT_FORMS = {'signup': 'Book_store.forms.PersoUserRegisterForm'}
My account/signup.html template just refers to {{form.as_p}} and it doesn't display the extra fields specified in PersouserRegisterForm just the default ones
I don't see what I'm missing, Thanks for reading
EDIT: And Signing up fail because it violates not-null constraint for date_of_birth
You are overriding the save method but not saving user with the date_of_birth field .
def save(self, request):
user = super(PersoUserRegisterForm, self).save(request)
user.date_of_birth = self.cleaned_data['date_of_birth']
user.save()
return user

Django user model username (unique=False)

I have seen many of the solution but none of it seems to be answering the question.
My user model is using AbstractUser right now. Is there a way to make username(unique = False)
Since i wanted to allow user to ba able to have duplication of same username, as the login im am using is by email address and password
Below is the code that i tried but doesnt work.
Code:
class MyUser(AbstractUser):
username = models.CharField(max_length=30, unique=False)
error:
customuser.MyUser: (auth.E003) 'MyUser.username' must be unique because it is named as the 'USERNAME_FI ELD'
Try to specify email as username field with USERNAME_FIELD attribute:
class MyUser(AbstractUser):
username = models.CharField(max_length=30, unique=False)
USERNAME_FIELD = 'email'
class MyUser(AbstractUser):
username = models.CharField(max_length=30, unique=False)
email = models.EmailField(max_length=255, unique=True)
USERNAME_FIELD = 'email'
A non-unique username field is allowed if you use a custom authentication backend that can support it.
If you want to use django's default authentication backend you cannot make username non unique.
You will have to implement a class with get_user(user_id) and authenticate(request, **credentials) methods for a custom backend.
You can read it in the documentation here. https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#specifying-custom-user-model

DRF Serializer Relations / JSON API Framework

*Update 1: Attempts with corresponding errors now shown in serializer code
*Update 2: I've narrowed the issue to a conflict with the JSON Api framework I'm using
I'm fairly new to Python and Django and I'm struggling quite a bit with relations. I'm able to create the relations in the database however I've been unable to serialize the models together for a response object, despite trying every method in the documentation which seems very straightforward. My goal is to return both models from an APIView on login and if this code doesn't error I get a response with just the user model. The errors vary based on the different techniques and are similar to what other users get when having trouble with relations, however their fixes haven't solved my problem.
I'm wondering if there's something obvious I'm not doing right. I'm happy to share more code but I'm really at a loss as to how to proceed.
Fyi: My models are in different apps which is why the fk reference is users.User
Models:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=50, unique=True)
password = models.CharField(max_length=100)
name = models.CharField(max_length=50)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
has_business = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['password']
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
def __str__(self):
return self.email
class Business(models.Model):
name = models.CharField(max_length=50, unique=True)
address = models.CharField(max_length=100)
phone = models.CharField(max_length=20)
description = models.CharField(max_length=500)
user = models.ForeignKey(
'users.User',
related_name='business',
unique=False,
on_delete=models.CASCADE
)
has_driver = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
Serializer:
class UserSerializer(serializers.ModelSerializer):
token = srs.SerializerMethodField()
biz = BusinessSerializer(many=True, read_only=True,
source='business.id')
error: just returns user model
biz = BusinessSerializer(read_only=True, many=True, source='business')
error: ForeignKey' object has no attribute
biz = serializers.RelatedField(many=True, read_only=True)
error: 'User' object has no attribute 'biz'
biz = serializers.ReadOnlyField(source='businesses.id')
error: RelatedManager object at 0x7fed496fe208> is not JSON
serializable
biz = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
error:'User' object has no attribute 'biz'
class Meta:
model = models.User
fields = ('id', 'email', 'password', 'name', 'token', 'has_business', 'biz')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = models.User(
email = validated_data['email'],
name = validated_data['name'],
phone = validated_data['phone'],
location = validated_data['location'],
)
user.set_password(validated_data['password'])
user.save()
return user
View:
class LoginUser(APIView):
permission_classes = (permissions.AllowAny,)
resource_name = 'users'
def post(self, request):
email = request.POST.get('email', False)
password = request.POST.get('password', False)
if(email and password):
lu = UserAuth()
authenticated_user = lu.auth(email=email, password=password)
if authenticated_user is not None:
if authenticated_user.is_active:
serializer = serializers.UserSerializer(authenticated_user, context={'request': request})
return Response(serializer.data, status=status.HTTP_200_OK)
You have to use the related_name in your serializer.
You can take a look here
In this example you can see that the Track Model (analog to your Business Model) has the field called album with related_name='tracks'
Then in the AlbumSerializer (the analog to your User Model):
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
So, in summary you have to use your related_name business
This is the example with a StringRelatedField, but you can of course return the complete object using this
Turns out I was using a beta version (pip install djangorestframework-jsonapi==2.0.0-beta.1) of the JSON API Framework and it wasn't handling relations very well. I picked the beta because it was the one the documentation lists which is kind of odd. You have to dig a bit for the more stable versions. I'm embarrassed to say how long that took me to figure out.
Here's the stable version as of this day: pip install djangorestframework-jsonapi==2.2.0

Django custom user model with unique_together on the email

I'm trying to create a custom User model in my Django app, the problem is I get an error saying email must be unique (fair enough!), however, I need email and company together to be unique, as I may have the same email but registered to a different company.
I get the following error:
ERRORS:
site.SiteUser: (auth.E003) 'SiteUser.email' must be unique because it is named as the 'USERNAME_FIELD'.
Here is my model:
class SiteUser(models.Model):
company = models.ForeignKey(Company)
email = models.EmailField(max_length=254)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(auto_now=False, auto_now_add=True)
objects = SiteUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
unique_together = ('company', 'email',)
You're missing the unique=True in your email field definition.
The filed that is used in the USERNAME_FIELD should have this argument as explained in the django doc on USERNAME_FIELD.
It should be like this:
email = models.EmailField(max_length=254, unique=True)
Add auth.E003 to the SILENCED_SYSTEM_CHECKS setting. This will allow manage.py to run. And I think you should add W004 warning to this list too:
SILENCED_SYSTEM_CHECKS = ['auth.E003', 'auth.W004']

I've created a custom User model. Can the CustomUserManager I create also extend django.contrib.gis.db.GeoManager?

I'm implementing a custom user model that looks roughly like this:
class User(AbstractBaseUser):
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['username']
id = models.IntegerField(DjangoUser, primary_key=True, null=False)
username = models.CharField(max_length=20, unique=True, db_index=True, null=False)
flag = models.PointField(srid=4326, geography=True, null=True)
# objects = models.GeoManager()
def __unicode__(self):
return self.username
As a requirement, I need to continue support for queries against the flag field (which is only possible with objects = models.GeoManager()). I understand that with creating a new User model that extends AbstractBaseUser, I need to create a new UserManager extending CustomUserManager.
Is there any way to retain the functionality GeoManager provides while using a custom UserManager?
I apologize for the lack of appropriate terminology. This is my first django app. Feel free to fill in terms I could have used.
Why don't you create CustomUserManager class inherited from GeoManager and add the required user-related functionality there:
class CustomUserManager(GeoManager):
def create_user(self, ...):
...
class User(AbstactBaseUser):
...
objects = CustomUserManager()

Categories