Django delete an object - python

Good day, I have a Model object, which contains the user who created it, as well as the one specified in a form.
with localhost:800/delete/int:title_id/ the one object is deleted.
Question:
How can I make sure that user a user who also created the object can delete it.
If userXY owns the object with ID 123 and he calls localhost:800/delete/123, the object will be deleted.
But if OtherNutzerYX calls localhost:800/delete/123, the object will not be deleted because the object does not belong to him, but to UserXY.
models.py
class NewTitle(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
default=None,
null=True,
on_delete=models.CASCADE,
)
title = models.CharField(max_length=200)
creator_adress = models.GenericIPAddressField(null=True)
id = models.BigAutoField(primary_key=True)
def __str__(self):
return str(self.title)
urls.py
path('delete/<int:title_id>/', views.title_delete),
views.py
def title_view(request):
titles = NewTitle.objects.all()
custom_title_id = random.randint(1111, 1111111111111)
if request.method == 'POST':
form = NewTitleForm(request.POST, instance=NewTitle(user=request.user))
if form.is_valid():
obj = form.save(commit=False)
obj.creator_adress = get_client_ip(request)
obj.id = custom_title_id
while NewTitle.objects.filter(id=obj.id).exists():
obj.id = random.randint(111, 11111111111)
obj.save()
return redirect('/another')
else:
form = NewTitleForm()
return render(request, 'test.html', {'form': form, 'titles': titles})
def title_delete(request, title_id):
if #WHAT CODE HERE?:
NewTitle.objects.filter(id=title_id).delete()
else:
return redirect('https://example.com')
return HttpResponseRedirect('/another')
The relevant code is the title_delete function.
I don't know what to write in the if statement. It has something to be like: 'if user of the title id == the user who is requesting the url == delete the model' 'else = if the user is not the owner, go to example.com and do not delte the model'
We can get the user who requested the url with request.user now we just need to check if the request.user is equal to the owner of the model. How?
(By the way, if there are better ways to create a custom ID for each model or you notice something else about my code that could be better, please tell me)
Thanks for your help :-)

As you have mentioned you only want to delete the title object if the object is created by a currently logged-in user.
Here is how you can achieve it
def title_delete(request, title_id):
user_title = NewTitle.objects.filter(id=title_id,
user=request.user)
if user_title:
user_title.delete()
else:
return redirect('https://example.com')
return HttpResponseRedirect('/another')
you can also call .first() after filter if you are sure that your user base only one title
user_title = NewTitle.objects.filter(id=title_id,
user=request.user).first()

Related

Direct assignment to the forward side of a many-to-many set is prohibited. Use Class.set() instead

When I try to save Class which is in many to many relationship django throws the following error
TypeError at /class-create/
Direct assignment to the forward side of a many-to-many set is prohibited. Use Class.set() instead.
My views.py looks like this
#login_required()
def create_class(request):
tea_user = request.user.username
validate = teacher_validation(tea_user)
if validate:
if request.method == 'POST':
Link = request.POST.get('link')
Subject = request.POST.get('Subject')
Class = request.POST.get('Class')
teacher_user = Teacher.objects.get(User=request.user)
teacher = Teacher.objects.get(id=teacher_user.id)
created_class = Online_Class(Link=Link, Subject=Subject, Created_by =teacher, Class=Class)
created_class.save()
return render(request, 'online_class/Teacher/class-create.html')
else:
messages.warning(request, 'Sorry You Dont have Permission to access this page')
return redirect('logout')
And my models.py file looks like this
class Online_Class(models.Model):
Created_by = models.ForeignKey(Teacher, on_delete=models.DO_NOTHING)
Class = models.ManyToManyField(Classes)
Subject = models.CharField(max_length=100)
Link = models.CharField(max_length=200)
Joined_by = models.ManyToManyField(Student, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
choice = (('Yes','Yes'),('No', 'No'))
Class_Ended = models.CharField(choices=choice, default='No', max_length=10)
Please help me figure it out
You can not set class=Class in:
created_class = Online_Class(Link=Link, Subject=Subject, Created_by =teacher, Class=Class)
since Class is a ManyToManyField, and thus can not be set like that, you first create the OnelineClass, and then add an entry (or more entries) with created_class.Class.set(&hellip):
#login_required
def create_class(request):
tea_user = request.user.username
validate = teacher_validation(tea_user)
if validate:
if request.method == 'POST':
Link = request.POST.get('link')
Subject = request.POST.get('Subject')
Class = request.POST.get('Class')
teacher_user = Teacher.objects.get(User=request.user)
teacher = Teacher.objects.get(id=teacher_user.id)
created_class = Online_Class.objects.create(
Link=Link,
Subject=Subject,
Created_by =teacher
)
created_class.Class.set([Class])
return render(request, 'online_class/Teacher/class-create.html')
else:
messages.warning(request, 'Sorry You Dont have Permission to access this page')
return redirect('logout')
Note: normally a Django models, just like all classes in Python are given a name in PascalCase, not snake_case, so it should be: OnlineClass instead of Online_Class.
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: class instead of Class.
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.

How to add a new record with a guest (anonymous) user in Django

I am a beginner in Django and I am very need your help.
Part of code:
models.py
from django.contrib.auth.models import User
class Author(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=200)
def __str__(self):
return self.user.username
class hardware(models.Model):
hostname = socket.gethostname()
login_username = getpass.getuser()
user = User.username
hardware_name = models.CharField(max_length=20)
order_no = models.CharField(max_length=10)
price = models.DecimalField(max_digits=5,decimal_places=2)
confirm = models.BooleanField(default=False)
login_user = models.ForeignKey(Author, on_delete=models.CASCADE)
computer_login_user = models.CharField(max_length=10,default=login_username)
computer = models.CharField(max_length=30,default=hostname)
def __str__(self):
return self.order_no
views.py
def get_author(user):
qs = Author.objects.filter(user=user)
if qs.exists():
return qs[0]
return None
def new_record(request):
form = OrderForm(request.POST or None, request.FILES or None)
if form.is_valid():
author = get_author(request.user)
form.instance.login_user = author
form.save()
return redirect(all_records)
context = {
'form': form
}
return render(request, 'orders/form.html', context)
I will try to explain my problem briefly.
Computers are in public places (productions) and anyone can add new record. That why in the table is info about hostname, who is login on computer and login user.
So it works well when the user is logged in to the system, but there is a problem when a new record tries to add an unlogged user (guest). Is an error "'AnonymousUser' object is not iterable".
I know that request.user is empty now.
Ok, Now questions...
How to add "guest" user and add it if noone is login?? How to add a new record if the user is not logged in??
I am sorry for very long post and Thanks for all suggestions.
So, if I understand correctly, you can do this a few ways:
The easiest way is to simply set the login_user field nullable and blank or,
Create a "guest user" and "guest author" in your Django database that is not active (is_active is set to False so they can't log in) and all anonymous users are assigned that User and Author instance the database.
As mentioned, the simplest method would be just to set the login_user field as nullable and blank, like such:
login_user = models.ForeignKey(Author, on_delete=models.CASCADE, blank=True, null=True)
And if your get_author() returns None, then simply leave that column blank, though this might affect other parts of your application if an Author or User object is required elsewhere.
Another way to do it in your get_author() method using a "guest" user:
def get_author(user):
if user.is_anonymous:
guest_user = User.objects.get(username="guest") # or whatever ID or name you use for the placeholder user that no one will be assigned
guest_author = Author.objects.get_or_create(user=guest_user)
return guest_author
else:
return Author.objects.get(user=user)
In this option, you'd need to set your department field in Author to blank and null or set a default like:
class Author(models.Model):
user = ...
department = ...(..., default="none")
or
class Author(models.Model):
user = ...
department = ...(..., blank=True, null=True)
Yet another option might be to create a new "guest" user for each action:
import random
import string
def randomString(stringLength):
letters = string.ascii_letters
return ''.join(random.choice(letters) for i in range(stringLength))
def get_author(user):
if user.is_anonymous:
random_username = f"{randomString(10)}_guest"
random_email = f"{randomString(5)}_guest#example.com"
guest_user = User.objects.create(username=random_username, is_active=False, email=random_email...)
guest_author = Author.objects.create(user=guest_user, department="none")
return guest_author
else:
return Author.objects.get(user=user)
Thank you for your help and time. I chose the second solution after little change.
def get_author(user):
if user.is_anonymous:
guest_user = User.objects.get(username="guest") # or whatever ID or name you use for the placeholder user that no one will be assigned
qs = Author.objects.filter(user=guest_user)
if qs.exists():
return qs[0]
return None
else:
qs = Author.objects.filter(user=user)
if qs.exists():
return qs[0]
return None
Now is little better and working well.
When I use exactly your method was little mistake: "ValueError: Cannot assign "(<Author: guest>, False)": "hardware.login_user" must be a "Author" instance."
So, anyway thank you again.

How to implement PUT method in Django

How can I enable the user to generate only one instance of an object “bet” with a POST method and modify it through a PUT method (for example)
forms.py
class BetForm(forms.ModelForm):
team1_score = forms.IntegerField(min_value=0, max_value=15)
team2_score = forms.IntegerField(min_value=0, max_value=15)
match = forms.ModelChoiceField(queryset=Match.objects.only('id'))
class Meta:
model = Bet
fields = ('team1_score', 'team2_score', 'match')
models.py
class Bet(models.Model):
match = models.ForeignKey(Match, on_delete=models.CASCADE, related_name='+')
team1_score = models.PositiveIntegerField(default=0)
team2_score = models.PositiveIntegerField(default=0)
def __str__(self):
return (str(self.match))
views.py
def post(self, request):
form = BetForm(request.POST)
if form.is_valid():
form.save()
team1_score = form.cleaned_data.get('team1_score')
team2_score = form.cleaned_data.get('team2_score')
match = form.cleaned_data.get('match')
form = BetForm()
return redirect ('home')
args = {'form': form, 'team1_score': team1_score, 'team2_score': team2_score, 'match': match}
return render(request, self.template_name, args)
Enable the user to generate only one instance of an object “bet”...
For that, you want to add a user field to your Bet model. Here you will save a reference to the user making the request.
class Bet(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, related_name='bets', blank=True)
match = models.ForeignKey(Match, related_name='bets')
team1_score = models.PositiveIntegerField(default=0)
team2_score = models.PositiveIntegerField(default=0)
class Meta:
unique_together = ('user', 'match')
def __str__(self):
return (str(self.match))
Notice the unique_together option which makes sure a user can only create a single Bet instance for a given match.
modify it through a PUT method (for example)
Django does not automatically parse the body for PUT requests like it does for POST. Browsers normally issue POST request on forms submission. If you still want to solve it using PUT, check this post (pun intended).
Parsing Unsupported Requests (PUT, DELETE, etc.) in Django
My suggestion is to modify your post view so it accepts an optional parameter bet_id. This you can define in urlpatterns. The view would then look like this one. You retrieve the bet if bet_id is provided and pass it to the form. This way it understands the user is modifying it.
def post(self, request, bet_id=None):
if bet_id:
bet = Bet.objects.get(pk=bet_id)
form = BetForm(request.POST, instance=bet)
else:
form = BetForm(request.POST)
if form.is_valid():
bet = form.save(commit=False)
bet.user = request.user
bet.save()
# Do what you want here
Notice that we are not saving the form immediately (commit=False), so we could assign it to a user later on. This user is the logged in user from the request object.

get() returned more than one Friend -- it returned 2

I'm trying to create a django app in which one user can add other user as Friend. Here's what I did,
models.py,
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='all', null=True)
views.py
# view for adding or removing friends
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
data = Friend.objects.get(current_user=request.user)
frnds = data.users.all()
new_friend in frnds:
data.users.remove(new_friend)
else:
data.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
# Displaying frinends,
def following(request, id=None):
my_friend, created = Friend.objects.get_or_create(current_user_id=id)
all_friends = my_friend.users.all()
return render(request, 'all/follow.html', {'all_friends': all_friends})
This code was working fine until I added friends from 1 account only, but when I added several friends from several accounts it started showing an error get() returned more than one Friend -- it returned 2!.
How can we fix that? Thank You!
in change friend function this line of code change like this
new_friend = User.objects.filter(pk=pk).first()
Try this...
Delete all Friend instances in admin and change model to:
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.OneToOne(User, related_name='friend', on_delete=models.CASCADE, null=True)
then views should be:
# view for adding or removing friends
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
friends = request.user.friend.users.all()
new_friend in friends:
request.user.users.remove(new_friend)
else:
request.user.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
# Displaying frinends,
def following(request, id=None):
my_friend, created = Friend.objects.get_or_create(current_user_id=id)
all_friends = my_friend.users.all()
return render(request, 'all/follow.html', {'all_friends': all_friends})
If you use many_to_many to record friends relationship,it should be better set model as:
current(OneToOne) users(ManyToMany)
If you use ForeignKey to record friends relationship,it should be better set model as:
current(ForeignKey) user(ForeignKey)
Update
If you can't change models anymore,just change code to:
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
data = Friend.objects.filter(current_user=request.user)
has_user = False
for x in data:
if new_friend in x.users.all():
has_user = True
x.users.remove(new_friend)
if not has_user:
firend = Friend.objects.filter(current_user=request.user).first()
if friend:
friend.users.add(new_friend)
else:
friend = Friend.objects.create(current_user=request.user)
friend.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])

Django - Trying to a field that is not in the form but is in the model

Right now I have a model.form with one field nothing more, but the model has 3 fields(see reference down below) and 1 of them set by the form, one has a default to false as it should be and the last one I will set in the view but it won't correctly do it and idk why.
Model & Form.
class TeamMembership(models.Model):
user = models.ForeignKey(User)
team = models.ForeignKey(Team)
leader = models.BooleanField(default=False)
class TeamSettings_acceptForm(forms.ModelForm):
class Meta:
model = TeamMembership
fields = ('user',)
View
#login_required
def teamsettings_accept_applications(request, team_pk):
if request.method == 'POST':
logged_in_user = get_object_or_404(User, pk=request.user.pk)
requested_team = get_object_or_404(Team, pk=team_pk)
for member in requested_team.teammembership_set.all().order_by('-leader'):
if member.user.pk == request.user.pk and member.leader:
formaccept = TeamSettings_acceptForm(request.POST)
accepteduserid = formaccept.data['user']
teamapplications = TeamApplication.objects.all().filter(from_user=accepteduserid).count()
if teamapplications > 1:
messages.success(request, "Error")
return redirect('teamsettings_applications', team_pk=team_pk)
else:
if formaccept.is_valid():
teamapplications = TeamApplication.objects.all().filter(from_user=accepteduserid)
teamapplications.update(accepted=True)
formaccept.team = requested_team.pk
formaccept.save()
messages.success(request, "User has now been added to your team!")
return redirect('teamsettings_applications', team_pk=team_pk)
it should create a new row with that data and update the others.
All I get in return from Django is
staff_teammembership.team_id may not be NULL
You haven't processed the Model form into an instance of a model yet. So formaccept doesn't know what team is here, because it's an instance of TeamSettings_acceptForm which doesn't have team as a field. To fix this change the is_valid code:
if formaccept.is_valid():
teamapplications = TeamApplication.objects.all().filter(from_user=accepteduserid)
teamapplications.update(accepted=True)
# New code here
new_team_membership = formaccept.save(commit=false)
new_team_membership.team = requested_team.pk
new_team_membership.save()
messages.success(request, "User has now been added to your team!")
return redirect('teamsettings_applications', team_pk=team_pk)
Using commit=false is really handy with Modelforms.

Categories