I am trying to get current logged in user through my model so that I can only see the current user in my order page dropdown:
I have gone through a lot of documents which state that it is not that easy or feasible to get current logged in user in model.
I have tried other method like getting AUTH_USER_MODEL but it is returning admin level users as well so not solving the problem.
I am also sending the current logged in user from my views file but dont know how to access it inside form class, able to access it in init but dont know how it can be accessed in class.
models.py :
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from django.contrib.auth import get_user_model
from django.http import HttpResponse,HttpRequest
class Customer(models.Model):
name = models.CharField(max_length=200,null=True)
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Order(models.Model):
product = models.ForeignKey(Product,null=True, on_delete=models.SET_NULL)
#customer = models.ForeignKey(settings.AUTH_USER_MODEL,null=True, on_delete=models.SET_NULL)
customer = models.ForeignKey(Customer,null=True, on_delete=models.SET_NULL)
date_ordered = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=20,default= 'PENDING')
def __str__(self):
return str(self.customer.id)
forms.py :
class createorderform(ModelForm):
def __init__(self,*args,**kwargs):
self._instance=kwargs.pop('instance',None)
super().__init__(*args,**kwargs)
#def __init__(self,instance):
# self.user = instance
# super().__init__(instance)
class Meta:
model=Order
fields="__all__"
exclude=['status']
Views.py
def placeorder(request,i):
try:
products = Product.objects.all()
customer = Customer.objects.get(id=i)
print("Customer:",customer)
#form=createorderform(prod=products,cust=customer)
form=createorderform(instance=customer)
#form=createorderform()
if(request.method=='POST'):
form=createorderform(request.POST,instance=customer)
if(form.is_valid()):
form.save()
return redirect('/')
context={'form':form}
return render(request,'app_name/placeorder.html',context)
except:
print("Error occurred : {exec}".format(exec=traceback.format_exc()))
What I am getting is all the users:
What I want is to only show the current user in drop down.
Please help or guide me in the right direction.
Thanks in advance!!
Something like
from django.forms import ModelForm
from django.views.generic import CreateView
class CreateOrderForm(ModelForm):
class Meta:
model = Order
exclude = ['status', 'customer']
def __init__(self, **kwargs):
self.customer = kwargs.pop('customer')
super().__init__(**kwargs)
def save(self, commit=True):
self.instance.customer = self.customer
return super().save(commit=commit)
class PlaceOrderView(CreateView):
model = Order
form_class = CreateOrderForm
template_name = 'app_name/placeorder.html'
def get_form_kwargs(self):
return {
**super().get_form_kwargs(),
'customer': Customer.objects.get(user=self.request.user),
}
should be enough.
So I have kind of tried one solution and this one work, although I have tried it earlier but in between 100's of documents and solutions the easy one got lost :
In my forms.py :
class createorderform(ModelForm):
def __init__(self,*args,**kwargs):
self._instance=kwargs.pop('instance',None)
super().__init__(*args,**kwargs)
#def __init__(self,instance):
# self.user = instance
# super().__init__(instance)
class Meta:
model=Order
fields="__all__"
exclude=['customer','status']
I excluded the customer field and instead populated it in my views. py:
def placeorder(request,i):
try:
products = Product.objects.all()
customer = Customer.objects.get(id=i)
print("Customer:",customer)
#form=createorderform(prod=products,cust=customer)
form=createorderform(instance=customer)
#form=createorderform()
if(request.method=='POST'):
form=createorderform(request.POST,instance=customer)
if(form.is_valid()):
curr_user = form.save(commit=False)
curr_user.customer = customer
curr_user.save()
return redirect('/')
context={'form':form}
return render(request,'app_name/placeorder.html',context)
except:
print("Error occurred : {exec}".format(exec=traceback.format_exc()))
So I am overriding the save() and building my current logged in user in the view itself while saving it. It's storing the same user in db as well.
EDIT:
def placeorder(request,i):
try:
products = Product.objects.all()
customer = Customer.objects.get(id=i)
print("Customer:",customer)
#form=createorderform(prod=products,cust=customer)
form=createorderform()
#form=createorderform(instance=customer)
if(request.method=='POST'):
form=createorderform(request.POST)
#form=createorderform(request.POST,instance=customer)
if(form.is_valid()):
curr_user = form.save(commit=False)
curr_user.customer = customer
curr_user.save()
return redirect('/')
context={'form':form}
return render(request,'app_name/placeorder.html',context)
except:
print("Error occurred : {exec}".format(exec=traceback.format_exc()))
So I have change my form object and I am not sending my current logged in user to forms any more so just creating the object without "instance=customer" which got my current logged in user.
Now we don't need to handle the instance in init method of createorderform form :
class createorderform(ModelForm):
#def __init__(self,*args,**kwargs):
# self._instance=kwargs.pop('instance',None)
# super().__init__(*args,**kwargs)
#def __init__(self,instance):
# self.user = instance
# super().__init__(instance)
class Meta:
model=Order
fields="__all__"
exclude=['customer','status']
Related
I want to build a REST API where user can do operations on objects, based on their permissions. Consider a record that represents a car - it contains the license number, the type of the car and extra information. Also consider the following user system:
Owners - Who own the car object. Can modify it and delete it.
Editors - Who can only modify the object properties.
Viewers - Can only view the object properties.
Each record can contain multi owners/editors/viewers (The user who created the object should be automatically the owner). Also, owners can add or remove editors/viewers. In my head, I see it as a list of owners/editors/viewers.
So in case of a GET request, I want to be able to return all objects that the user has permissions for, separated into those three categories.
So under my api app, I have the following code:
The models.py file contains:
class CarRecord(models.Model):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
The serializers.py file contains:
class CarRecordSerializer(serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
class Meta:
model = CarRecord
fields = ('__all__')
In view.py I have:
class CarRecordViews(APIView):
def get(self, request):
if not request.user.is_authenticated:
user = authenticate(username=request.data.username, password=request.data.password)
if user is not None:
return Response(data={"error": "invalid username/password"}, status=status.HTTP_401_UNAUTHORIZED)
# return all records of cars that user some type of permission for
Now, I want to get all the records of user that he has permissions to query (along with their permission type). I thought of adding a three extra fields under CarRecord - each one is a list of users that contains that permission type. But I'm not sure if it's the "Django way". So wanted to consult first with SO.
EDIT: I tried to add the following field to my CarRecord class:
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[])
Also I added:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username']
lass CarRecordSerializer(serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = UserSerializer(many=True)
class Meta:
model = CarRecord
fields = ('__all__')
And the way I create the CarRecordSerializer instance is:
serializer = CarRecordSerializer(data=request.data)
But I get:
{
"error": {
"owners": [
"This field is required."
]
}
}
How to make it work? I guess is my problem is how to serialize a ManyToMany object?
EDIT2: My second attempt is:
class CarRecord(models.Model):
date_created = models.DateTimeField()
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[]))
creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)
# ...
class CarRecordSerializer(serializers.ModelSerializer):
date_created = serializers.DateTimeField(default=datetime.now(timezone.utc))
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
creator = serializers.StringRelatedField()
owners = serializers.PrimaryKeyRelatedField(many=True,read_only=True)
class Meta:
model = CarRecord
fields = '__all__'
def create(self, validated_data):
self.owners = [self.context['creator']]
record = CarRecord(**validated_data, creator=self.context['creator'])
record.save()
return record
# ...
# In post method:
serializer = CarRecordSerializer(data=request.data, context={ 'creator': user })
But now, in GET method, I filter the owners list with the user and it can't find the objects:
> CarRecord.objects.filter(owners=user)
<QuerySet []>
Also, in the Admin section I see that all of the objects automatically have all the users in the owners/editors/viewers lists. Why is that? Owners should contain only the user that created the record and editors and viewers should be empty lists. In another query, owner can add additional owners/editors/viewers.
Here is the solution I might think is the right one
class CarRecord(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = models.ManyToManyField(User, related_name='car_owners')
creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)
class CarRecordSerializer(serializers.ModelSerializer):
creator = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), required=False)
owners_details = UserSerializer(source='owners', many=True, read_only=True)
class Meta:
model = CarRecord
fields = '__all__'
def create(self, validated_data):
try:
new_owners = validated_data.pop('owners')
except:
new_owners = None
car_record = super().create(validated_data)
if new_owners:
for new_owner in new_owners:
car_record.owners.add(new_owner)
return car_record
In views.py
from rest_frameword import generics
from rest_framework import permissions
class CustomCarRecordPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method == 'GET':
return True
elif request.method == 'PUT' or request.method == 'PATCH':
return request.user == obj.creator or request.user in obj.owners.all()
elif request.method == 'DELETE':
return request.user == obj.creator
return False
class CarRecordListCreate(generics.ListCreateAPIView):
permission_classes = (IsAuthenticated, )
serializer_class = CarRecordSerializer
queryset = CarRecord.objects.all()
def post(self, request, *args, **kwargs):
request.data['creator'] = request.user.id
return super().create(request, *args, **kwargs)
class CarRecordDetailView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (CustomCarRecordPermissions, )
serializer_class = CarRecordSerializer
lookup_field = 'pk'
queryset = CarRecord.objects.all()
models is self explanatory;
In CarRecord serializers we set creator as required False and primary key related field so that we can supply request user id before create as shown in views.py post method.
In Detail view we set our custom permission; If the request is GET we allow permissions. But if the request is PUT or PATCH the owners and the creator are allowed. But if it is a delete request only creator is allowed.
I think the django-rest-framework-guardian package fits here. This package is based on django-guardian.
django-guardian is an implementation of object permissions for Django providing an extra authentication backend.
There is no change on your models.py
You should change serializers.py and views.py.
For example, your serializer should look like this
from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin
class CarRecordSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
class Meta:
model = CarRecord
fields = ('__all__')
def get_permissions_map(self, created):
current_user = self.context['request'].user
readers = Group.objects.get(name='readers')
editors = Group.objects.get(name='editors')
owners = Group.objects.get(name='owners')
return {
'view_car_record': [current_user, readers, owners],
'change_car_record': [current_user, editors],
'delete_car_record': [current_user, owners]
}
and your views should look like this:
from rest_framework_guardian import filters
class CarRecordModelViewSet(ModelViewSet):
queryset = CarRecord.objects.all()
serializer_class = CarRecordSerializer
filter_backends = [filters.ObjectPermissionsFilter]
Edit settings.py like this:
INSTALLED_APPS = [
'rest_framework',
'guardian',
]
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"guardian.backends.ObjectPermissionBackend",
]
You can define filter backends globally in your settings, too:
REST_FRAMEWORK = {
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
"rest_framework_guardian.filters.ObjectPermissionsFilter",
],
}
Don't forget! If you define the ObjectPermissionsFilter in the settings.py, your all views are affected by this filter.
If you want to restrict post request per user, you shoul implement custom permission class, like this:
from rest_framework import permissions
class CustomObjectPermissions(permissions.DjangoObjectPermissions):
"""
Similar to `DjangoObjectPermissions`, but adding 'view' permissions.
"""
perms_map = {
'GET': ['%(app_label)s.view_%(model_name)s'],
'OPTIONS': ['%(app_label)s.view_%(model_name)s'],
'HEAD': ['%(app_label)s.view_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
Check this link to get the detailed information for the CustomObjectPermissions
You can write permission class car owner user.
Your model.
class CarRecord(models.Model):
date_created = models.DateTimeField()
type = models.CharField(max_length=50)
license = models.CharField(max_length=50)
owners = models.ManyToManyField(User, related_name='car_owners', verbose_name=('owners'), default=[]))
creator = models.ForeignKey(User,on_delete=models.DO_NOTHING)
Permission class permission.py
from rest_framework.permissions import BasePermission,
from cars.models import CarRecord
class isCarAccess(BaseCommand):
def has_permission(self, request, view):
if request.method == 'OPTIONS':
return True
check_user = CarRecord.objects.filter(owners__in=[request.user])
return request.user is not None and request.user.is_authenticated and check_user
this permission class will check that does user exists, user is authenticated and as well the user belongs to the card record or not.
And you can pass this permission in your view.
from .permission import isCarAccess
from .models import CarRecord
class CarRecordViews(APIView):
permission_classes = [isCarAccess]
def get(self, request):
car_record = CarRecord.objects.filter(owners__in=[request.user])
# return all records of cars that user some type of permission for
and your settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"oauth2_provider.contrib.rest_framework.OAuth2Authentication",
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.TokenAuthentication",
),
}
I'm a beginner and I'm trying to create a small network project in which users can follow each other. I have implemented the follow button right, so it updates my models and displays proper info to users, but I can't get unfollow to work properly. I'm guessing it's something to do with the way I implemented follow model (with many to many field), but I'd like to implement it this way for practice... Anyhow, here's the code:
Models:
class User(AbstractUser):
pass
class Follow(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_follow")
following = models.ManyToManyField(User, blank=True, related_name="followers")
And view:
def users(request, username):
"""Displaying user profiles"""
if request.method == "POST":
user = request.user
profile = User.objects.get(username=username)
follow = Follow(user=user)
follow.save()
if "unfollow" in request.POST:
profile.followers.remove(user)
follow.following.remove(profile)
return HttpResponseRedirect(reverse('users', args=(username,)))
elif "follow" in request.POST:
follow.following.add(profile)
return HttpResponseRedirect(reverse('users', args=(username,)))
This code yields in: "ValueError at /users/test
Cannot query "admin": Must be "Follow" instance." at the profile.followers.remove(user) line...
Playing with it in shell I found out (at least I think so) that the line under it (follow.following.remove(profile) - which by the way was there before I tried with the profile.followers.remove(user)) removes the profile from Follow model, but for some reason it is not by itself updated in the Users model (for followers) ???
from django.db import models
# Create your models here.
class User(models.Model):
name = models.CharField(max_length=40)
pwd = models.CharField(max_length=40)
def __str__(self):
return self.name
class Follow(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
another_user = models.ManyToManyField(User, related_name='another_user')
def __str__(self):
return self.user.name
============================================================================
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .models import User, Follow
# Create your views here.
def index(request):
if 'user' in request.session:
return render(request, 'index.html')
else:
return redirect('login')
def profile(request, user_name):
user_obj = User.objects.get(name=user_name)
session_user = User.objects.get(name=request.session['user'])
session_following, create = Followers.objects.get_or_create(user=session_user)
following, create = Followers.objects.get_or_create(user=session_user.id)
check_user_followers = Followers.objects.filter(another_user=user_obj)
is_followed = False
if session_following.another_user.filter(name=user_name).exists() or following.another_user.filter(name=user_name).exists():
is_followed=True
else:
is_followed=False
param = {'user_obj': user_obj,'followers':check_user_followers, 'following': following,'is_followed':is_followed}
if 'user' in request.session:
return render(request, 'profile.html', param)
else:
return redirect('index')
def follow_user(request, user_name):
other_user = User.objects.get(name=user_name)
session_user = request.session['user']
get_user = User.objects.get(name=session_user)
check_follower = Followers.objects.get(user=get_user.id)
is_followed = False
if other_user.name != session_user:
if check_follower.another_user.filter(name=other_user).exists():
add_usr = Followers.objects.get(user=get_user)
add_usr.another_user.remove(other_user)
is_followed = False
return redirect(f'/profile/{session_user}')
else:
add_usr = Followers.objects.get(user=get_user)
add_usr.another_user.add(other_user)
is_followed = True
return redirect(f'/profile/{session_user}')
return redirect(f'/profile/{session_user}')
else:
return redirect(f'/profile/{session_user}')
=============================================================================
User This For Reference...Follow And Unfollw Logic
I'm new to programming and my first language/stack is Python and Django. I have figured out how to create a dropdown menu in my Script form that is pointing to a different class "Patient" but I can't figure out how to only show me data that the current user created. I'm confused if I should set this in my models.py, forms.py or in the views.py? Here is what I have that I think should be working but it is not. (Tried setting in the views.py)
Models.py
class Patient(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
patient_name = models.CharField(max_length=40, unique=True)
def __str__(self):
return self.patient_name
class Script(models.Model):
author = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, verbose_name='Primary Patient')
So my patient field is my dropdown and it is looking at the Patient class grabbing the patient name string. I only want patient_name entry's that this user created in the dropdown.
Views.py
class ScriptCreateView(LoginRequiredMixin, CreateView):
model = Script
template_name = 'script_new.html'
success_url = reverse_lazy('script_list')
fields = (
'patient',
'drug_name',
'drug_instructions',
'drug_start_day',
'drug_start_time',
'drug_hours_inbetween',
'drug_num_days_take',
)
#This sets user created fields only??
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
author=self.request.user
)
#This sets the author ID in the form
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form
)
Forms.py
class ScriptForm(forms.ModelForm):
class Meta:
model = Script
fields = '__all__'
#This is requiring user login for any of these views??
def __init__(self, user=None, *args, **kwargs):
super().__init__(*args, **kwargs)
if user:
self.fields['patient'].queryset = Patient.objects.filter(author=user)
I'm sure it is my lack of experience here but I thought by setting the function def get_queryset in the view that it would only show me user created data. I have googled a bunch and I really can't find the clear answer on this.
In your views.py file initialize form like this please
<form or form_class> = Form(request.POST, user=request.user)
I had to add the last form.fields query below in the view which filtered items only created by "author" which is what I was looking for:
def get_form(self):
form = super().get_form()
form.fields['drug_start_day'].widget = DatePickerInput()
form.fields['drug_start_time'].widget = TimePickerInput()
form.fields['patient'].queryset = Patient.objects.filter(author=self.request.user)
return form
My problem in GIF
Instead of updating the user's rating DRF creating new.
Maybe i made a mistake in serializer?
I wrote documentation but i dont kwon where i wrong.
My code:
views.py:
class CreateReviewView(APIView):
def post(self, request):
review = CreateReviewSerializer(data= request.data)
if review.is_valid():
review.save()
return Response(status=201)
class CreateRatingView(APIView):
def get_user(self, request):
user= request.user
if user =="AnonymousUser":
return "noname in CreateRaringView"
return user
def post(self, request):
serializer = CreateRatingSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(user=self.get_user(request))
return Response(status=201)
else:
return Response(status=400)
serializers.py:
class Meta:
model = Rating
fields = ('star','movie')
def new(self,validated_data):
rating = Rating.objects.update_or_create(
user= validated_data.get('user',None),
movie= validated_data.get('movie',None),
defaults={'start': validated_data.get("star")}
)
return rating
models.py:
class Rating(models.Model):
"""Рейтинг"""
user = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name="Пользователь",related_name='user')
star = models.ForeignKey(RatingStar, on_delete=models.CASCADE, verbose_name="Звезда",related_name="star")
movie = models.ForeignKey(Movie, on_delete=models.CASCADE, verbose_name="Фильм",related_name="movie")
def __str__(self):
return f"{self.star} - {self.movie}"
class Meta:
#unique_together = ['user','movie','star']
verbose_name = "Рейтинг"
verbose_name_plural = "Рейтинги"
According to documentation, Calling .save() will either create a new instance, or update an existing instance, depending on if an existing instance was passed when instantiating the serializer class:
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
In your case you are only passing new data and missing existing instance.
def post(self, request):
serializer = CreateRatingSerializer(data=request.data)
I renamed def new(self,validated_data): to def create(self,validated_data): in serializers.py and all started working. >.<
I have a problem after a bigger upgrade from Django 1.7 to 1.10 and to Django Rest Framework 3.5.4. The problem is when I try to access the endopint /claims it returns a 500 Error with the text: IntegrityError at /claims (1048, "Column 'user_id' cannot be null")
In urls.py that route is added like this:
url('^claims', v.ClaimList.as_view(), name='claim_list')
And the relevant part from the views.py file is:
class ClaimSerializer(serializers.ModelSerializer):
company_name = s.ReadOnlyField(source="ad.company.name")
company_address = s.ReadOnlyField(source="ad.company.address")
ad_thumbnail = ThumbnailField(source="ad.picture", size="200x200", read_only=True)
class ClaimSerializerDeep(ClaimSerializer):
class Meta:
exclude = ('user',)
model = m.Claim
depth = 2
class ClaimSerializerFlat(ClaimSerializer):
class Meta:
exclude = ('user',)
model = m.Claim
#permission_classes((IsAuthenticated,))
class ClaimList(Limitable, generics.ListCreateAPIView):
model = m.Claim
def get_queryset(self):
tab = self.request.GET.get("tab", "active")
q = m.Claim.objects.filter(user=self.request.user.pk)
return self.limit(q)
def pre_save(self, obj):
obj.user = self.request.user
def get_serializer_class(self):
if self.request.method == "POST":
return ClaimSerializerFlat
else:
return ClaimSerializerDeep
And the claim model is:
class Claim(models.Model):
ad = models.ForeignKey("Ad")
user = models.ForeignKey(settings.AUTH_USER_MODEL)
created = models.DateTimeField(auto_now_add=True)
redeemed = models.BooleanField(default=False)
def save(self, *args, **kwargs):
increase_claimed = False
super(Claim, self).save(*args, **kwargs) # here is where it crashes
Do you have any ideas what could cause the problem?
user = models.ForeignKey(settings.AUTH_USER_MODEL)
Your model requires that a user gets set, but obviously it isn't (anymore).
The question is why?
Looking at your code this seens to be the relevant line:
def pre_save(self, obj):
obj.user = self.request.user
Assuming you also upgraded DRF it is quite plausible that this doesn't work anymore: http://www.django-rest-framework.org/topics/3.0-announcement/#changes-to-prepost-save-hooks
So you have to adapt your code and use perform_create as explained in the docs.