I have a User and a Deal model as shown below. The User model has a 'favorites' field which is a many to many relationship with the Deal model.
I'm trying to allow a user to save a Deal to their Favorites. I have tested both the favorite and remove_favorite views and both are doing exactly what they are supposed to do.
Here's My Issue -- The conditional statement on my deal_detail.html page which checks to see if the current deal on the page is a favorite of the logged in user doesn't seem to be working.
{% if deal_detail in user.favorites %}
I'm just having a hard time wrapping my head around how to check this.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, error_messages={'unique':"This email has already been registered."})
username = models.CharField(max_length=40, default='')
first_name = models.CharField(max_length=40, default='', blank=True)
last_name = models.CharField(max_length=40, default='', blank=True)
date_joined = models.DateTimeField(default=timezone.now)
favorites = models.ManyToManyField(Deal, related_name='favorited_by', null=True, blank=True)
class Deal(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=140, unique=True)
description = models.TextField(default='')
My corresponding views look like this:
def deal_by_detail(request, slug):
deal_detail = Deal.objects.get(slug=slug)
user = request.user
return render(request, 'deals/deal_detail.html', {'deal_detail': deal_detail, 'user': user})
#login_required(login_url='/accounts/sign_in/')
def favorite(request, pk):
if request.method == 'POST':
favorite = Deal.objects.get(pk=pk)
user = request.user
user.favorites.add(favorite)
messages.add_message(request, messages.INFO, 'Deal Favorited.')
return redirect('home')
#login_required(login_url='/accounts/sign_in/')
def remove_favorite(request, pk):
if request.method == 'POST':
favorite = Deal.objects.get(pk=pk)
user = request.user
user.favorites.remove(favorite)
messages.add_message(request, messages.INFO, 'Deal Removed.')
return redirect('home')
My form deal_detail.html looks like this:
{% if deal_detail in user.favorites %}
<form id="favorite{{deal_detail.id}}" method="POST" action="{% url 'deals:favorite' deal_detail.id %}">
{% csrf_token %}
<input type="hidden" name="supporttype" />
<input type="submit" value="Add Deal to Favorites" />
</form>
{% else %}
<form id="favorite{{deal_detail.id}}" method="POST" action="{% url 'deals:remove_favorite' deal_detail.id %}">
{% csrf_token %}
<input type="hidden" name="supporttype" />
<input type="submit" value="Remove Deal From Favorites" />
</form>
{% endif %}
I believe you are just missing the .all after the manytomany field. Then it should be able to run the check and function as you intended
{% if deal_detail in user.favorites.all %}
Related
I am trying to make a form that auto populates a many-to-many relation for my user model. The goal is to have a submit button that adds the views instance object (the SingelWorkout object) to a many-to-many field relation within my user model.
The view accurately displays the correct object, and the form appears as intended within the template. I do not wish for the user to see the many-to-many field selection. Aside from the submit button, I am trying to have all logic to occur on the backend. How would I assign an object instance to a field within a form? Would this occur in the views.py or the forms.py?
Here is why my user model looks like:
class FitnessUser(AbstractUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField(max_length=60)
age_category = models.ForeignKey(
AgeGroup,
on_delete=models.CASCADE,
blank=True,
null=True
)
goal = models.IntegerField(default=1 ,choices=Purpose.choices)
weight = models.CharField(max_length=30)
height = models.CharField(max_length=30)
gender = models.ForeignKey(
Gender,
on_delete=models.CASCADE,
blank=True,
null=True
)
exercise_frequency = models.IntegerField(default=1 ,choices=Frequency.choices)
template_id = models.ForeignKey(
Workout_template,
on_delete=models.CASCADE,
blank=True,
null=True
)
completed_workouts = models.ManyToManyField(SingleWorkout)
def get_absolute_url(self):
return reverse('detail', args=[self.id])
This is my form in forms.py:
class CustomWorkoutChangeForm(UserChangeForm):
class Meta(UserChangeForm):
model = FitnessUser
fields = ('completed_workouts',)
exclude = ('completed_workouts',)
UserChangeForm.password = None
This is how my view looks:
class WorkoutUpdateView(LoginRequiredMixin, UpdateView):
model = SingleWorkout
template_name = 'workout/daily_view.html'
form_class = CustomWorkoutChangeForm
success_url = reverse_lazy("template")
def get_context_data(self, **kwargs):
context = super(WorkoutUpdateView, self).get_context_data(**kwargs)
context['workout'] = SingleWorkout.objects.get(slug = self.kwargs['slug'])
return context
My html template looks like this:
{{workout}}
<br>
workout:
<br>
{{ workout.exercise_1 }}
<br>
{{ workout.description_1 }}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Confirm">
</form>
Figured out a solution. I created a view that gets the instance object based on the objects url slug, and also gets the user by its pk. From there is adds the instance object to the users many to many field, then redirects back to the previous page.
New view created:
def update_workout_data(request, slug):
workout = SingleWorkout.objects.get(slug=slug)
endUser = FitnessUser.objects.get(pk = request.user.pk)
endUser.completed_workouts.add(workout)
endUser.save()
return redirect(reverse('daily', kwargs={'slug':workout.slug}))
Updated HTML appearance. I've also altered the html and its detail view so that the update link will redirect to a separate update view, depending on the need to add/remove the relation.
{% block content %}
Daily View
<br>
{{exercise}}
<br>
workout:
<br>
<br>
{% if exercise.name in workouts %}
<h5>Workout Already Completed</h5>
<form method="POST" action="{% url 'remove' slug=exercise.slug %}">
{% csrf_token %}
<button type="submit">Reset</button>
</form>
{% else %}
<form method="POST" action="{% url 'update' slug=exercise.slug %}">
{% csrf_token %}
<button type="submit">Complete</button>
</form>
{% endif %}
{% endblock content %}
Updated Detail View
def get_context_data(self, **kwargs):
context = super(WorkoutDetailView, self).get_context_data(**kwargs)
user = FitnessUser.objects.get(pk = self.request.user.pk)
context['exercise'] = SingleWorkout.objects.get(slug = self.kwargs['slug'])
context['workouts'] = {}
for workout in user.completed_workouts.all():
context['workouts'][workout.name] = workout
return context
views.py
def student_login(request):
form = StudentLoginForm()
if request.method == 'POST':
form = StudentLoginForm(data=request.POST)
print(form.is_valid())
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
print(username)
user = authenticate(username=username,password=password)
if user is not None:
login(request,user)
return redirect('index')
else:
messages.error(request,'Invalid username or password!')
else:
messages.error(request,'Invalid username or password!')
context = {'form':form}
return render(request,'student_login.html',context)
models.py
class Student(models.Model):
name = models.CharField(max_length=100)
username = models.CharField(max_length=100,unique=True,default=None)
mobile = models.CharField(max_length=8)
email = models.CharField(max_length=200,unique=True)
password = models.CharField(max_length=200,default=None,unique=True)
total_books_due = models.IntegerField(default=0)
def __str__(self):
return self.name
forms.py
class StudentLoginForm(forms.ModelForm):
class Meta:
model = Student
fields = ['username','password']
widgets = {
'password':forms.PasswordInput(),
}
student_login.html
{% extends 'base.html' %}
<!-- {% load crispy_forms_tags %} -->
{% block content %}
<div class="container">
<br>
<h2>Student Login Form</h2>
<br>
<form method="POST" action="">
{% csrf_token %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %} <br>
{% for field in form %}
<p>{{ field.label }} </p>
<p>{{ field }} </p>
<br>
{% endfor %}
<br>
<input type="submit" class="btn btn-primary" value="Login">
</form>
</div>
{% endblock %}
I have been trying to change again and again but form.is_valid() is still returning False. I could not figure out the reason that the form is not valid because I have already specify the fields that I want to show and added the csrf_token. Could anyone help me to figure out where is the problem?
Try to use just a simple form and not a ModelForm, as ModelForms in the background work with creating and updating objects.
In this case, ModelForm is validating as if you are trying to create a new Student, causing the already exists failures.
For example, you can write a simple login form like this:
class StudentLoginForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={'autofocus': True}))
password = forms.CharField(
label='Password',
strip=False,
widget=forms.PasswordInput,
)
And then in your views, use it as is:
def student_login(request):
form = StudentLoginForm()
if request.method == 'POST':
form = StudentLoginForm(data=request.POST)
if form.is_valid():
user = authenticate(
username=form.cleaned_data.get('username'),
password=form.cleaned_data.get('password'),
)
if user:
login(request, user)
return redirect('index')
messages.error(request, 'Invalid username or password!')
context = {'form':form}
return render(request,'student_login.html',context)
can you please change this line
form = StudentLoginForm(data=request.POST)
to
form = StudentLoginForm(request.POST)
and also why you are validating the fields as you already validating using .is_valid()
function?
Just create a simple form with username and password. Also, the password field should not be unique if you are using plain text.
Better to use inbuilt User model provided by django. If you want to extend fields in User model, use AbstractUser and override the User model. See in documentation https://docs.djangoproject.com/en/3.2/topics/auth/customizing/
I want to separate the open sessions in my site so a user can't delete/modify objects from another account. If someone could explain me how do I do that, for example here with the delete function.
This is my code:
models.py
class User(AbstractUser): ##abstract user model
pass
class Auction(models.Model): ##these are the objects
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
name = models.CharField(max_length=64)
img = models.ImageField(upload_to='listing_image', null=True, default="/static/default_image.jpg")
description = models.TextField(blank=True)
starting_bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="auctions", null=True)
views.py
#login_required(login_url="login")
def delete(request, id):
auction = get_object_or_404(Auction, id=id)
if request.method == "POST":
auction.delete()
return redirect ('index')
return render(request, "auctions/delete.html", {
"auction": auction
})
html:
{{auction.user}}
<h1>{{ auction.name }}</h1>
<img src="{{ auction.img.url }}" width="600">
<h5>Starting price: ${{ auction.starting_bid }}</h5>
<form method="POST" enctype="multipart/form-data">
{%csrf_token%}
<button type="submit" class="btn btn-primary">Place a New Bid</button>
{{form.as_p}}
</form>
{%for bid in bids%}
<h3>${{ bid.new_bid}}</h3>
{%empty%}
No offers yet.
{%endfor%}
{{auction.description}}
<h2>Comments</h2>
<ul>
<p>New Comment</p>
{%for comment in comments%}
<li>{{comment.user}} - {{comment.created_on}}
<p><h4>{{comment.body}}</h4></p>
</li>
{%empty%}
<li>No comments yet.</li>
{%endfor%}
</ul>
<p>Edit
Delete</p>
You filter on the user as well:
#login_required(login_url='login')
def delete(request, id):
auction = get_object_or_404(Auction, id=id, user=request.user)
if request.method == 'POST':
auction.delete()
return redirect ('index')
return render(request, 'auctions/delete.html', {
'auction': auction
})
This will return a HTTP 404 error in case the .user of the Auction is not the logged in user, so they will not be able to remove the object.
In the template, you need to check if the user is the logged in one to show the buttons. But even if you somehow show buttons, the users will not be able to trigger the logic behnind it if you filter accordingly:
{% if user == auction.user %}
<p>Edit
Delete</p>
{% endif %}
I want to use feedback forms in my app. And I have FeedBack model and FeedBackForm form.
models.py:
class Feedback(models.Model):
name = models.CharField(max_length=150, blank=True)
email = models.EmailField(blank=True)
comment = RichTextField()
forms.py:
class FeedBackForm(forms.ModelForm):
class Meta:
model = Feedback
fields = ('name', 'email', 'comment')
I used FeedBackForm in my views,py file
views.py
def home(request):
if request.method == 'POST':
feedback_form = FeedBackForm(data=request.POST)
if feedback_form.is_valid():
feedback_form.save()
else:
feedback_form = FeedBackForm()
return render(request, 'home.html', {'feedback_form': feedback_form})
Now my question is: how can I use my variables in template? Instead of this 3 input tags. (Is there another easier way other than
{{ feedbackform.as_p }}
and
{% csrf_token %}
to call my form variables "name", "email" and "comment" as input). Thanks in anvance
<form action="." method="post">
<input type="text" name="name">
<input type="email" name="email">
<textarea name="comment"></textarea>
</form>
If I get your question right, you want to call form.name, form.email and form.comment in template? See more at https://docs.djangoproject.com/ja/1.9/topics/forms/#rendering-fields-manually
In my application, I used email and password for user authentication, which works fine. However, I want to offer the user the option of adding other information to their account like first names, last names, and dates of birth.
I have a change form in myapp.forms.py
class MyChangeForm(forms.ModelForm):
"""
Form for editing an account.
"""
first_name = forms.CharField(widget=forms.TextInput, label="First name")
last_name = forms.CharField(widget=forms.TextInput, label="Last name")
date_of_birth = forms.DateField(widget=forms.DateField, label="Date of birth")
class Meta:
model = MyUser
fields = ['first_name', 'last_name', 'date_of_birth']
def save(self, commit=True):
user = super(MyChangeForm, self).save(commit=False)
if commit:
user.save()
return user
in my views.py, I have the following method for updating
#login_required(login_url='/')
def update_user(request):
if request.method == 'POST':
form = MyChangeForm(request.POST, instance=request.user)
if form.is_valid():
user = form.save(commit=False)
user.save()
return HttpResponseRedirect('/')
else:
form = MyChangeForm(instance=request.user)
return render_to_response('update_user.html', context_instance=RequestContext(request))
and my update_user.html is as follows
{% extends 'user_base.html' %}
{% block content %}
<div class="col-sm-3 col-sm-offset-5">
<h1> Update User</h1>
<form method='POST' action='/update_user/'> {% csrf_token %}
<ul>
{{ form.as_table }}
</ul>
<input type='Submit' class='btn btn-primary btn-block'>
</form>
</div>
{% endblock %}
However, when I serve the file I see this:
As seen here, there's no way to enter my fields!
How can I fix this? It's probably easy, but I'm getting tunnel vision.
erip
Add form to the context, for example like this:
render('update_user.html', {'form': form})