How to select a OnetoOne field from a QuerySet in a view? - python

See all users (that don't have search_hidden enabled) view
#login_required
def users(request):
"""List all users page"""
t_users = User.objects.all()
users = t_users.usersettings.filter(search_hidden=False).select_related('user')
context = {'users': users}
return render(request, 'users/users.html', context)
UserSettings model
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserSettings(models.Model):
"""Stores the user's settings."""
user = models.OneToOneField(User, related_name='usersettings', on_delete=models.CASCADE)
public_profile = models.BooleanField(default=True)
search_hidden = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'usersettings'
def __str__(self):
return f"{self.user}'s settings"
#receiver(post_save, sender=User)
def create_user_usersettings(sender, instance, created, **kwargs):
if created:
UserSettings.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_usersettings(sender, instance, **kwargs):
instance.usersettings.save()
All users have a UserSettings model tied to them when their accounts are created. In my view, I want to select all users that have search_hidden disabled, however what I've tried doesn't work.The error 'QuerySet' object has no attribute 'usersettings' is displayed whenever the page is requested. I probably need to select each user and retrieve the settings, but I don't know how to do that in an efficient manner.

iliya commented that filtering using t_users.objects.filter(search_hidden=False) would return users where search_hidden is not true in their settings object.

Related

Retrieving list of all online users using Django-online-users

I would like to retrieve a list of all users online on my, online meaning they have carried out some sort of activity on the app over the last 2 minutes.
I am noticing however, that my list only shows people who have logged in as an admin (atleast from what I am observing).
I am using the package django-online-users for this
I created a view with a queryset that would retrieve all online users who have carried out some activity on the app
class OnlineUsers(viewsets.ModelViewSet):
queryset = online_users.models.OnlineUserActivity.get_user_activities(timedelta(seconds=120))
serializer_class = OnlineUsersSerializer
My OnlineUsersSerializer class:
class OnlineUsersSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
This is my profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=50,blank=True)
country = models.CharField(max_length=50, blank=True)
bio = models.CharField(max_length=500, blank=True)
profile_pic = models.ImageField(upload_to='profile/%Y/%m/%d',
default='media/placeholder.png',
blank=False,
null=False)
#we are hooking create_user_profile and save_user profile methods to the User model whenever a save event occurs. This kind of signal is called post_save
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I created an API end-point that I would use to retrieve all online users from my React app:
path('online_users',OnlineUsers.as_view({'get':'list'}), name='online_users'),
Regardless of whether I login with a different user, I am only able to see one user (my admin account).
UPDATE
Based on the top answer I received this is what I tried:
class OnlineNowMixin:
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
user = request.user
if not user.is_authenticated:
return
online_users.models.OnlineUserActivity.update_user_activity(user)
I added this class to all my viewsets:
class OnlineUsers(OnlineNowMixin, viewsets.ModelViewSet):
...
class UserViewSet(OnlineNowMixin, viewsets.ModelViewSet):
...
class UpdateProfileView(OnlineNowMixin, generics.UpdateAPIView):
...
class ProgrammingChallengeView(OnlineNowMixin, ReadOnlyModelViewSet):
...
class GetAllUsers(OnlineNowMixin, viewsets.ModelViewSet):
...
However, the API end point I created still only shows users who logged as opposed to user who have been logged in through authentication (ie. received an access & refresh token).
This is what i am using for authentication:
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls, user):
token = super().get_token(user)
token['username'] = user.username
token['first_name'] = user.first_name
token['last_name'] = user.last_name
token['country'] = user.profile.country
token['city'] = user.profile.city
token['bio'] = user.profile.bio
token['photo'] = json.dumps(str(user.profile.profile_pic))
return token
class CustomTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
Your users aren't processed as online by the middleware probably because you are using Django Rest Framework (DRF). DRF performs the authentication on the view layer, out of the various builtin authentication classes only SessionAuthentication will have the user authenticated at the middleware level.
What you can do to solve this problem is create a mixin to be used by all your views / viewsets. This mixin should override the initial method and do what the OnlineNowMiddleware from django-online-users does:
from online_users.models import OnlineUserActivity
class OnlineNowMixin:
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
user = request.user
if not user.is_authenticated:
return
OnlineUserActivity.update_user_activity(user)
class OnlineUsers(OnlineNowMixin, viewsets.ModelViewSet):
...

Add logged user as author of model, but keep ForeignKey

I want when add article, current logged user to be added as author, I'm also using ForegnKey to user and want to keep it, but right now throw error:
objects/models.py:
from django.db import models
from users.models import ProfileUser
class Object(models.Model):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
address = models.CharField(max_length=300)
content = models.TextField()
def __str__(self):
return f"{self.title}"
objects/forms.py:
from django import forms
from .models import Object
class ObjectForm(forms.ModelForm):
class Meta:
model = Object
fields = [
'title',
'address',
'content',
]
objects/views.py:
def add_object(request):
form = ObjectForm(request.POST or None)
if form.is_valid():
obj = form.save(commit=False)
obj.author = request.user
obj.save()
return redirect('home')
context = {
'form': form
}
return render(request, "add_object.html", context)
Also I rewrite default django user model:
users/models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class ProfileUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.URLField()
#receiver(post_save, sender=User) # Still don't know how, but next rows create ProfileUser when User is created
def create_user_profile(sender, instance, created, **kwargs):
if created:
ProfileUser.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profileuser.save()
def __str__(self):
return f"{self.user}"
Error:
Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x04CD3B30>>": "Object.author" must be a "ProfileUser" instance.
The author must be a ProfileUser instance as the error message says, because you declared the author field (of the Object model) as a ProfileUser.
In add_object method, obj.author needs to be a ProfileUser instance, therefore you should get that instance by looking up the user.
def add_object(request):
form = ObjectForm(request.POST or None)
if form.is_valid():
obj = form.save(commit=False)
obj.author = ProfileUser.objects.get(user=request.user)
obj.save()
return redirect('home')
context = {
'form': form
}
return render(request, "add_object.html", context)
If the current user doesn't always exist for the ProfileUser instance, you could use the get_or_create method for the author. Read more about that here

Django Admin Create Form Inline OneToOne

I have the following model:
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
last_password_reset = models.DateTimeField(auto_now_add=True)
needs_password_reset = models.BooleanField(default=True)
image_url = models.URLField(max_length=500, default=None, null=True, blank=True)
I am trying to inline this into the admin. I have the following:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserCreationForm
class UserProfileInline(admin.StackedInline):
"""User profile inline."""
model = Profile
can_delete = False
verbose_name_plural = "Profile"
class CustomUserCreationForm(UserCreationForm):
"""Create user form."""
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email")
class CustomUserAdmin(UserAdmin):
"""Custom user admin."""
add_form = CustomUserCreationForm
inlines = (UserProfileInline,)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
This is working fine up to a point; when I go to create user, I can see the inlined profile information. However, when I try to submit the form I get the following error (on /admin/auth/user/add/):
psycopg2.errors.NotNullViolation: null value in column "user_id" violates not-null constraint
Why is the user_id field not getting populated in the inline form? How can I set this attribute to the id of the user created by the form?
Turns out I needed to remove some signals I had written for automatic profile creation:
# Create a profile for new users
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_user_profile(sender, instance=None, created=False, **kwargs):
if created:
Profile.objects.get_or_create(user=instance)
# Update profile on user change
#receiver(post_save, sender=User)
def save_user_profile(sender, instance=None, created=False, **kwargs):
instance.profile.save()

Custom column in Django users profile

I want to add custom column in Django admin, on Users (/auth/user/) section.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birthday = models.DateField()
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
And in admin.py I have this code:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'Custom fields'
class CustomUser(UserAdmin):
inlines = (ProfileInline, )
list_diplsay = ('birthday')
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super(CustomUser, self).get_inline_instances(request, obj)
admin.site.unregister(User)
admin.site.register(User, CustomUser)
I've read here that list_display should do all the work, but in my case it doesn't work. I don't see any changes in my admin panel since I've added that line.
Where is the problem? Thanks!
Edit: Fixed by changing list_diplsay with list_display. Now I get this: type object 'User' has no attribute 'birthday'. Any ideas?
You have a typo: list_diplsay should be list_display. You should probably also add a trailing comma to your list value: ('birthday',). This ensures you end up with a tuple and not a single value.
Edit:
Since the birthday field doesn't belong to the User you'll have to add a method to look it up in your admin class:
class CustomUser(UserAdmin):
list_display = ('birthday',)
def birthday(self, obj):
return obj.profile.birthday
(I have only shown the relevant parts of the admin class; your existing inlines etc. should stay.)

Extending User model in django

I am trying to register a user in django extending the auth module. I read from here and there and now I am all over the place and dont know what I am doing.
Initially what I wanted to do is register a user with information name, email, mobile and password. since mobile is not in the default django auth I tried to extend auth.
here is the code.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class CustomerUserProfile(models.Model):
mobile = models.CharField(max_length = 20)
user = models.ForeignKey(User, unique=True)
def __str__(self):
return "%s's profile" % self.user
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = CustomerUserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from neededform.models import CustomerUserProfile
class CustomerRegistrationForm(UserCreationForm):
email = forms.EmailField(required = True)
mobile = forms.CharField(max_length = 20)
class Meta:
model = User
fields = ('username','email','mobile','password1','password2')
views.py
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from django.core.context_processors import csrf
from neededform.forms import CustomerRegistrationForm
from neededform.models import CustomerUserProfile
from django.contrib.auth.models import User
def register(request):
if request.method == 'POST':
form = CustomerRegistrationForm(request.POST)
if form.is_valid():
f = form.save()
return HttpResponseRedirect('/registered/')
else:
args = {}
args.update(csrf(request))
args['form'] = CustomerRegistrationForm()
return render_to_response('User_Registration.html', args ,context_instance = RequestContext(request))
This code upon execution makes an entry in auth_user table and an entry in CustomerUserProfile but the mobile coloumn is blank always.
what else am I suppose to add in the code ?
PS. please can some one explain me what is this code doing
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = CustomerUserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User)
as I copied it from somewhere I want to know how this works actually.
Thank you.
This is a 2-year old question but I am surprised that there was no correct answer.
The mobile field is not saved in your profile, simply because you create your CustomerUserProfile via the post_save signal, and the mobile field is not forwarded by the post_save signal.
I would suggest to simply update your CustomerUserProfile when you handle form.is_valid() in your register function.

Categories

Resources