Custom user model not working in foreign key - django

I have created a custom user model which is working fine.
The problem is when access the user from foreign key, it throws me :
DETAIL: Key (author_id)=(51) is not present in table "auth_user".
My custom user is userauth, clearly the model still looking for the original User model instead of the custom one.
Here is what I did:
#settings.py
AUTH_USER_MODEL = 'userauth.UserAuth'
#models.py
from django.conf import settings
User = settings.AUTH_USER_MODEL
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
author = models.ForeignKey(User, null=True,blank=True,default=None)
#admin.py
class AdminPost(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if getattr(obj, 'author', None) is None:
obj.author = request.user
obj.save()

I would do something like this instead (don't bother assigning variables):
models.py:
from django.conf import settings
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
# You don't need 'default=None'
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)

Related

Model register save user created Django admin

I'm using Django admin, and I have a model in which I have a created_user property. The problem is that I don't know how to register the user from the Django admin.
How can I do this?
In order to register the user who performed a create/update on a model in the django admin, override the save_model method in the admin view.
Assuming the model in question is SomeRandomModel defined as follows
#in models.py
from django.db import models
from django.conf import settings
class SomeRandomModel(models.Model):
some_field = models.CharField(max_length=200)
another_field = models.CharField(max_length=200)
created_user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True, null=True,
related_name="%(app_label)s_%(class)s_created_by_set",
editable=False,
on_delete=models.PROTECT)
updated_user = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True, null=True,
related_name="%(app_label)s_%(class)s_updated_by_set",
editable=False,
on_delete=models.PROTECT
)
To associate created_user and updated_user on SomeRandomModel with the current logged in user whenever a CRUD is performed in the Django admin, override the save_model method in admin.py as follows:
#admin.py
from models import SomeRandomModel
from django.contrib import admin
#admin.register(SomeRandomModel)
class SomeRandomModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if not obj.pk:
obj.created_user = request.user #user who created the object should only be set once
obj.updated_user = request.user #user that last updated the object can be set on each save
super().save_model(request, obj, form, change)
Reference: How to associate model with current user while saving?
in On Admin Site you should see the automatically added ModeAdmins for Users and Groups
in Users ModelAdmin you can create new User.

making an API that adds instances to ManyToMany fields of a model in django rest framework

I am making a movie watching website in which there are users and films and the user model has a ManyToMany Field that references the film model. it's called WatchList and an authenticated user can add any movie they want to this watchlist.
My problem is that I want an API that only gets the ID of a film and adds it to the user's watch list.
these are my models and serializers and I am trying to make a view to implement this API.
# models.py
class Film(models.Model):
filmID = models.AutoField(primary_key=True)
title = models.CharField(max_length=150)
# ...
class User(AbstractBaseUser, PermissionsMixin):
userID = models.AutoField(primary_key=True)
username = models.CharField(max_length=100, unique=True, validators=[RegexValidator(regex="^(?=[a-z0-9._]{5,20}$)(?!.*[_.]{2})[^_.].*[^_.]$")])
email= models.EmailField(max_length=100, unique=True, validators=[EmailValidator()])
name = models.CharField(max_length=100)
watchList = models.ManyToManyField(Film)
objects = UserManager()
USERNAME_FIELD = 'username'
# serializers.py
class WatchListSerializer(serializers.ModelSerializer):
class FilmSerializer(serializers.ModelSerializer):
model = Film
fields = ('filmID', 'title',)
read_only_fields = ('filmID', 'title')
film_set = FilmSerializer(read_only=True, many=True)
class Meta:
model = get_user_model()
fields = ('userID', 'film_set')
read_only_fields = ('userID',)
# views.py
class WatchListAddView(...):
pass
The serializer can be changed. but this kind of shows what I want the api to be. the authentication validation part is already taken care of, so imagine that any request to the view is from an authenticated user.
I would not recommend patching this directly and instead create a separate endpoint for adding removing data to this field.
In your case it would look like this. I show just a small working example, you can adjust it to your needs
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
#action(detail=True,
methods=['POST'])
def add_film_to_watch_list(self, request, **kwargs):
film = get_object_or_404(klass=Film, filmID=kwargs.get('filmID'))
user = self.get_object()
user.watchList.add(film)
return Response("Success")

Create a separate user type

I am working on an intranet web application which needs two types of users. Normal users that can be setup from django admin and specific type of users -
Employees.
I have the following model for Employee type user.
class Employee(models.Model):
emp_name = models.CharField(max_length=500)
slug = models.SlugField(unique=True, default='')
location = models.CharField(max_length=200)
email = models.EmailField()
experience = models.TextField(blank=True)
primary_skill = models.ManyToManyField(PrimarySkill)
secondary_skill = models.ManyToManyField(SecondarySkill)
I tried having a OneToOneField like this as per the official doc and
this article:
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_employee(sender, instance, created, **kwargs):
if created:
Employee.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_employee(sender, instance, **kwargs):
instance.employee.save()
I realized that this is the opposite of what I want. Every time a User
is created from the admin, there was an entry created in the
app_employee table.
What I want is this:
Every time an Employee is created, I need a User created.
An Employee can be created using a separate signup form, say emp_signup
How do I approach this scenario?
I have achieved this using a custom user based on AbstractUser inspired by this article.
class CustomUser(AbstractUser):
pass
class Employee(CustomUser):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
# other fields
In settings.py, I then add the following key:
AUTH_USER_MODEL = 'myapp.CustomUser'
And wherever I need to refer the User class, I use get_user_model(), which will substitute our custom user, in views and forms as follows:
views.py
from django.contrib.auth import get_user_model
def user_profile(request):
User = get_user_model()
user = get_object_or_404(User, username=request.user.username)
return render(request, 'user/user_profile.html', {
'site_user': user
})
forms.py
class SignUpForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('username', 'email', 'password1', 'password2',)

django - relations between models

i have two model which are Post and Profile. i am keepin blog datas which are title,body,owner,slug etc in Post. and keepin' user profile settings which are slogan,email,website etc. in Profile
in my index.html page i display user profile infos and post lists in same page. so ;
i need to connect these two models each other. when someone goes to 127.0.0.1/blog/username (with or without login) all the data which are belong to user named 'username' must be there.
here is my models.py:
class Profile(models.Model):
slogan = models.TextField(blank=True)
twitter = models.CharField(max_length = 100,blank=True)
web_site = models.CharField(max_length=100,blank=True)
email = models.CharField(max_length = 100,blank=True)
def __unicode__(self):
return self.slogan
class Post(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length = 100)
body = models.TextField()
bodyPreview = models.TextField() #preview için body alanı
titlePreview = models.CharField(max_length=100) # preview için title alanı
slug = AutoSlugField(populate_from='title' ,unique=True)
posted = models.DateField(auto_now_add=True)
isdraft = models.BooleanField(default=False)
def __unicode__(self):
return self.title
#permalink
def get_absolute_url(self):
return ('view_blog_post',None,{'postslug':self.slug})
and my index view :
def index(request,username):
post_list = Post.objects.filter(owner__username=username).filter(isdraft=False).order_by("-posted")
p_index = Paginator(post_list,3) #anasayfa için pagination.[her sayfada 3 post]
page = request.GET.get('page')
try:
indexPag = p_index.page(page)
except PageNotAnInteger:
indexPag = p_index.page(1)
except EmptyPage:
indexPag = p_index.page(p_index.num_pages)
## i need to get user's profile datas here. ??
return render_to_response('index.html',
{'post_list':post_list,'p_index':indexPag,'profile':query},
context_instance = RequestContext(request))
I think you should change your Profile model and add a OneToOne relationship to the User model(for more info see here):
class Profile(models.Model):
user = models.OneToOneField(User)
...
class Posts(models.Model):
author = models.ForeignKey(User)
...
and then in your views you can do:
user = get_object_or_404(User, username=username)
post_list = Post.objects.filter(author=user).filter(isdraft=False).order_by("-posted")
return render_to_response('index.html',
{'post_list': post_list, 'user': user, ...}, ...)
And then in your template you are able to access the user's profile.More here
e.g {{user.get_profile.slogan}}
In your view:
def index(request, username):
user = User.objects.get(username=username)
return render(request, 'index.html', {'user':user})
In your template:
{{ user.post_set }}
and You will receive list of posts of current user.
The most natural way to achieve that is to add a OneToOne relation between your Profile model and the django's User model.
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
# More fields
class Article(models.Model):
author = models.ForeignKey(User)
# More fields
This way, you can access the profile data through a User object. Would be something like this:
user = User.objecets.get(username=username)
profile = user.profile
More info about this, you can read the django model fields documentation here
and you can also see this cool answer about the diference between the ForeignKey with unique=True and the OneToOneField
I hope this helps

Django: How to save current username to the database

I'm a new Django (1.3) user and I'm setting up the typical blog/article application. I have a model in which I'd like to save the current username (to see who created the article). I've implemented advice on this, but my code crashes the server with the message: "NameError: name 'User' is not defined". Here's my model:
from django.db import models
class Resort(models.Model):
name = models.CharField(max_length=300)
description = models.TextField()
location = models.CharField(max_length=300)
ski_hire = models.TextField()
weather = models.TextField()
piste_map = models.TextField()
webcam = models.TextField()
snow = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User)
def __unicode__(self):
return self.name
I have a sneaking suspicion that I need to override the save method in admin.py, but I'm not sure how to do this. Any advice would be greatly appreciated. Thanks!
You need to make sure you import user from django.contrib.auth.models in your models.py when using User in a foreign key.
In your admin.py you can use save_model:
class ResortAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.save()
Maybe You should import User class before assignment?
from django.contrib.auth.models import User