Django3.0 add request.user to form request - python-3.x

Let's say we have this model:
class Creation(models.Model):
title = models.CharField(max_length=DEFAULT_LENGTH)
url = models.CharField(max_length=DEFAULT_LENGTH)
date = models.DateTimeField('date published', default=datetime.date.today)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
def __str__(self):
return self.title
And let's use this view:
def submit(request):
FormSet = modelformset_factory(Creation, fields=('title', 'url'))
if request.method == 'POST':
formset = FormSet(request.POST, request.FILES)
if formset.is_valid():
obj = formset.save(commit=False)
obj.author = request.user
obj.save()
else:
formset = FormSet
return render(request, 'app/submit.html', {'formset': formset})
I saved formset to obj and added request.user and committed it to the database.
This doesn't work, because now django throws an error which says
'list' object has no attribute 'author'
which makes perfectly sense, because there is no author in the fields-list above.
But if I add 'author' to fields, another selectbox would be displayed while rendering the template via {{formset}}.
Maybe I could code my own template-code instead of using {{formset}} and omit the author, but I feel that there must be a more elegant way with Django3.0. Is there any?
All I want to do is to get the foreignkey author (see model above) filled with the logged in user.

Turns out that something is wrong with modelformset_factory(..).
If you use a proper CreationForm in models.py:
class CreationForm(ModelForm):
class Meta:
model = Creation
fields = ['title', 'url']
and use it in your views.py:
formset = CreationForm(request.POST)
if request.method == 'POST':
if formset.is_valid():
f = formset.save(commit=False)
f.author = request.user
f.save()
it works.

Related

Django Rest Framework Field Validation Issue

My subscription view is located inside of UserViewSet. I'm wondering why I'm getting
IntegrityError at /api/users/1/subscribe/
new row for relation "users_subscription" violates check constraint "prevent_self_subscription"
DETAIL: Failing row contains (11, 1, 1).
instead of proper json answer. Somehow SubscriptionSerializer field validation doesnt wish to work. Any thoughts?
models.py
class Subscription(models.Model):
user = models.ForeignKey(
User, related_name='subscriber',
on_delete=models.CASCADE)
author = models.ForeignKey(
User, related_name='subscribing',
on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=('user', 'author'),
name='unique_subscription'
),
models.CheckConstraint(
check=~models.Q(user=models.F('author')),
name='prevent_self_subscription'
)
]
serializers.py
class SubscriptionSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = models.Subscription
fields = ('author', 'user', )
validators = [
serializers.UniqueTogetherValidator(
queryset=models.Subscription.objects.all(),
fields=['author', 'user', ]
)
]
def create(self, validated_data):
return models.Subscription.objects.create(
user=self.context.get('request').user, **validated_data)
def validate_subscribing(self, value):
if self.context.get('request').user == value:
raise serializers.ValidationError(
'You cant subscribe to yourself!')
return value
views.py
#action(['post'], detail=True)
#permission_classes(permissions.IsAuthenticated)
def subscribe(self, request, *args, **kwargs):
author = get_object_or_404(models.User, id=kwargs['id'])
data = request.data.copy()
data.update({'author': author.id})
serializer = serializers.SubscriptionSerializer(
data=data, context={'request': request})
if request.method == 'POST':
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(
status=status.HTTP_201_CREATED,
data=self.get_serializer(author).data)
It was the wrong method name, field name is 'author' and method was validate_subscribing() instead of validate_author().

In Django Todo App how can each user have a distinct set of the task?

I am making a to-do app using Django. Currently, the problem I am facing is that no matter which user logs in, the same set of tasks appears, i.e. if a user adds some task for himself, they appear in every other user's to-do list. How can I modify my code so that each user can have a distinct set of tasks?
Here's some of my code:
Models
from django.db import models
# Create your models here.
class TodooModel(models.Model):
fd = models.TextField()
cr_dt = models.DateTimeField(auto_now_add=True)
Views.py
from django.shortcuts import render,redirect
from .models import TodooModel
# Create your views here.
def home(request):
if request.user.is_authenticated:
return render(request,"home.html")
else:
return redirect("user_login")
def create(request):
if request.user.is_authenticated:
if request.method == "POST":
fdk= request.POST.get("task")
data = TodooModel(fd=fdk)
data.save()
return render(request,"create.html",{"msg":"Added Succesfully"})
else:
return render(request,"create.html")
else:
return redirect("user_login")
def views(request):
if request.user.is_authenticated:
data = TodooModel.objects.get()
return render(request,"views.html",{"data":data})
else:
return redirect("user_login")
def delete(request,id):
if request.user.is_authenticated:
de = TodooModel.objects.get(id=id)
de.delete()
return redirect("views")
else:
return redirect("user_login")
You need to add new field to the TodooModel model,
When you create entry in TodooModel add created_by paramater.
And while filtering tasks you need to add created_by=user.
created_by = models.ForiegnKey() # add new field to your model.
data = TodooModel(fd=fdk, created_by=request.user) # in your create method
data = TodooModel.objects.filter(created_by=request.user) # in view method
Edit
Your code should be like,
models,
from django.contrib.auth.models import User
class TodooModel(models.Model):
fd = models.TextField()
cr_dt = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(User)
create method,
def create(request):
if request.user.is_authenticated:
if request.method == "POST":
fdk = request.POST.get("task")
data = TodooModel(fd=fdk, created_by=request.user)
data.save()
return render(request, "create.html", {"msg": "Added Succesfully"})
else:
return render(request, "create.html")
else:
return redirect("user_login")
view method,
def views(request):
if request.user.is_authenticated:
data = TodooModel.objects.filter(created_by=request.user)
return render(request, "views.html", {"data": data})
else:
return redirect("user_login")
Hope that helps.

How to filter Django serializer data?

I'm trying to filter data based on userName in JWT. This is how I've been trying to do it:
views.py:
class TestView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
request.user = user
person = Person.objects.filter(userName=request.user)
except ValidationError as v:
print("validation error", v)
return Response(person[0])
This works as I can get the Person data with print("Person: ", person[0]). The return Response(person[0]) however returns an error: TypeError: Object of type Person is not JSON serializable. I guess I could use a serializer class to return a valid JSON, am I right? I have this in my serializers.py:
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
I just don't know how to use this with my view. If I use serializer instead of person = Person.objects.filter(userName=request.user), how is the filtering supposed to be done?
Please correct me if I'm not on right track at all.
You can add serializer to view, provide query result to serializer and get serialized data:
class TestView(APIView):
permission_classes = (IsAuthenticated,)
serializer_class = TestSerializer # add serializer
def get(self, request):
token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
data = {'token': token}
try:
valid_data = VerifyJSONWebTokenSerializer().validate(data)
user = valid_data['user']
request.user = user
person = Person.objects.filter(userName=request.user).last()
data = self.serializer_class(instance=person).data # serialize query result
except ValidationError as v:
print("validation error", v)
raise v
return Response(data) # return serialized response
Here you can find some examples of using serializer with class based views.
Also note that your Person model might have security-sensitive fields like password, etc, so it's better to specify exact fields you need in serializer rather then use fields = '__all__'.
Are you using Django-Rest-Framework (based on your use of ModelSerializer)?
In pure Django, from the documentation on serializers, you can do something like:
from django.core import serializers
json_response = serializers.serialize("json", person[0])
return Response(json_response)
If you are using Django-Rest-Framework:
return Response(TestSerializer(person[0]).data)

NoReverseMatch at /NewListing

I have a form on my webpage for users to create a new listing. The form loads up okay but on submission an error comes up:
NoReverseMatch at /NewListing
Reverse for 'listing' with keyword arguments '{'listing': None}' not found. 1 pattern(s) tried: ['(?P<listing_id>[0-9]+)$']
The form does save the new listing and update the homepage with the new listing but I would like that once the listing is submitted, the user is taken to the new listing page and I can't seem to get that to work without this error popping up.
models.py
class Category(models.Model):
group = models.CharField(max_length=20)
def __str__(self):
return f"{self.group}"
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.TextField(max_length=64)
price = models.DecimalField(max_digits=9, decimal_places=2, validators=[MinValueValidator(0.99)])
image = models.URLField(max_length=200, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="categories")
def __str__(self):
return f"Product: {self.title} \nDescription: {self.description} \nCurrent Price: £{self.price}\nImage: {self.image} \nCategory: {self.category}"
forms.py
class NewListingForm(forms.ModelForm):
class Meta:
model = Listing
fields = ["title", "description", "price", "image", "category"]
title = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
description = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
price = forms.DecimalField(label='Starting Bid Price (£)')
image = forms.URLField(widget=forms.URLInput(attrs={'autocomplete':'off'}))
views.py
def listing(request, listing_id):
listing = Listing.objects.get(pk=listing_id)
return render(request, "auctions/listingPage.html", {
"listing": listing
})
def newListing(request):
if request.method == "POST":
form = NewListingForm(request.POST)
if form.is_valid():
title = form.cleaned_data['title']
description = form.cleaned_data['description']
price = form.cleaned_data.get('price')
image = form.cleaned_data['image']
category = form.cleaned_data['category']
listing = Listing(title=title, description=description, price=price, image=image, category=category)
form.save()
return HttpResponseRedirect(reverse("listing", kwargs={'listing': listing.id}))
else:
return render(request, "auctions/newListing.html", {
"form": form,
})
else:
form = NewListingForm()
return render(request, "auctions/newListing.html", {
"form": form
})
urls.py
urlpatterns = [
path("", views.index, name="index"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
path("<int:listing_id>", views.listing, name="listing"),
path("NewListing", views.newListing, name="newListing")
]
You're making a new Listing by hand from the form data, but never saving it, so it never gets an ID.
As a result, the URL that your code tries to compute as the redirection destination uses None instead of an integer for the listing ID, which blows up, because it's expecting an integer ID.
(You also save the modelform, but you don't do anything with the data that returns.)
Instead of this:
listing = Listing(title=title, description=description, price=price,image=image, category=category)
form.save()
Try just:
listing = form.save()
to let the ModelForm do all the heavy lifting and return saved object with an ID because it's in the database - that's what model forms are there for :o) https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/#the-save-method

How to implement django admin queryset filter with from extended user model class

I have extended Django default User model (just to save user state_id) class with following model.
class UserProfileInfo(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,)
state_id = models.IntegerField(blank=False)
def __str__(self):
return self.user.username
And now I want to filter the data by logged user's state_id. I tried to implement queryset filter but it does not do anything.
class VisVisitsAdmin(admin.ModelAdmin):
list_per_page = 10
list_display = ('visit_id','visit_no','user_name','mobile_number','program_name','state_name','district_name','block_name','school_name',)
list_filter = ('date_of_visit',)
def queryset(self, request):
qs = super(VisVisitsAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(state_id=request.user.UserProfileInfo.state_id)
def state_name(self, obj):
if obj.school_program:
return obj.school_program.school.cluster.block.district.state.name_of_state
state_name.short_description = 'state name'
You are trying to fetch the state_id from the userprofileinfo instance related to user object.
Everything seems fine but the instance is available as userprofileinfo and not UserProfileInfo , so change qs as :
qs.filter(state_id=request.user.userprofileinfo.state_id)

Resources