Create recommendation system from values in Django template - django

I'm trying to create a recommendation system in Django where I get the user with the highest rating. The rating is linked to the user as a Foreign-Key as seen in the code below:
models.py
class Bid(models.Model):
project = models.ForeignKey(
ProjectOrder, default="null", on_delete=models.CASCADE)
made_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
blank=True,
default="null",
on_delete=models.CASCADE
)
date = models.DateField(auto_now_add=True)
assign = models.BooleanField(default=False)
def __str__(self):
return self.project.title + " - " + self.made_by.username
class Meta:
ordering = ['-date']
class Rating(models.Model):
username = models.ForeignKey(User, on_delete=models.CASCADE)
score = models.IntegerField(default=0, validators=[
MaxValueValidator(5),
MinValueValidator(0),
])
review = models.TextField(max_length=250, blank=True, null=True)
reviewed_by = models.ForeignKey(User, on_delete=models.CASCADE,
blank=True,
null=True,
related_name="reviewed_by")
def __str__(self):
return str(self.username)
Template
{% for bid in client_bids_assign %}
<div
class="p-2 my-2 d-fle"
style="
border-radius: 10px;
gap: 10px;
box-shadow: 0 0.5px 2px rgba(128, 128, 128, 0.651);
"
>
<small id="">
<p class="badge bg-info">{{ bid.made_by }}</p>
<!-- star rating -->
<fieldset class="starability-result mx-auto" data-rating="{{rating}}">
Rated: {{rating}} stars
</fieldset>
<p class="text-muted">{{ bid.date }}</p>
</small>
<form method="POST">
{% csrf_token %}
<fieldset class="d-none">
<input type="text" name="bidId" id="bidId" value="{{ bid.id }}" />
</fieldset>
{% if bid.assign is False %}
<button
class="btn btn-sm btn-outline-success"
type="submit"
value="{{ bid.id }}"
>
Assign <i class="fa fa-check-circle"></i>
</button>
{% else %}
<button
class="btn btn-sm btn-outline-danger"
type="submit"
value="{{ bid.id }}"
>
Un-assign <i class="fa fa-exclamation-circle"></i>
</button>
{% endif %}
</form>
</div>
{% endfor %}
I am using a class based view therefore, this is how I get the rating.
this is a DetailView that handles a POST request and some other functionalities.
Views.py
class ProjectDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
model = ProjectOrder
#....
def test_func(self):
usergroup = None
usergroup = self.request.user.groups.values_list(
'name', flat=True).first()
if usergroup == "Writers" or usergroup == "Admin" or usergroup == "Clients":
return True
return False
# Get detail template according to user.
def get_template_names(self):
usergroup = None
usergroup = self.request.user.groups.values_list(
'name', flat=True).first()
if usergroup == "Writers":
return ["writer/projectorder_detail.html"]
elif usergroup == "Clients" or usergroup == "Admin":
return ["client/projectorder_detail.html"]
else:
return HttpResponseBadRequest("You are not allowed to access this page")
def get_context_data(self, **kwargs):
context = super(ProjectDetailView, self).get_context_data(**kwargs)
project = self.get_object()
user = self.request.user
client_bids = Bid.objects.filter(project__id=project.id)
client_bids_assign = Bid.objects.filter(
project__id=project.id, assign=True)
#get the rating of the user from the bid
def get_rating():
for bid in client_bids_assign:
user = bid.made_by #this user need to come from the bid inside the loop on the template
rating = user.rating_set.all()
agg = 0
count = 0
for rating in rating:
agg = agg + rating.score
count += 1
rating = agg/count
return rating
context.update({
'bForm': BidForm,
'client_bids': client_bids,
'client_bids_assign': client_bids_assign,
'rating': get_rating()
})
return context
# Post request handler (Bid form)
def post(self, request, *args, **kwargs):
bform = BidForm(request.POST)
# bidForm section
if bform.is_valid():
project = self.get_object()
bids = project.bid_set.all()
if bids.exists():
for bid in bids:
if bid.made_by == request.user:
bid.delete()
messages.info(request, "Bid reverted successfully")
else:
try:
new_bform = bform.save(commit=False)
new_bform.project = self.get_object()
new_bform.made_by = self.request.user
new_bform.save()
messages.success(
request, "Your bid has been submitted successfully")
except:
messages.error(
request, "Bid sending error, Try again later")
else:
try:
new_bform = bform.save(commit=False)
new_bform.project = self.get_object()
new_bform.made_by = self.request.user
new_bform.save()
messages.success(
request, "Your bid has been submitted successfully")
except:
messages.error(
request, "Bid sending error, Try again later")
context.update({
'bForm': BidForm,
'client_bids': client_bids,
'client_bids_assign': client_bids_assign,
'rating': get_rating()
})
# check usergroup to return appropriate page client/writer
if usergroup == "Writers":
return render(request, "writer/projectorder_detail.html", context)
else:
return render(request, "client/projectorder_detail.html", context)
my problem is that I can't seem to be able to get the user that submitted the bid from the template to be the user in the view whose rating I want to get. I have also tried having the mathematical operations in the template but this is not best practice and it seems to break the template i.e. parse error
Note: The Bid in the template loops through the current bids made
Thanks in advance.

Related

Django - User not passing through form

I am making an auction site and I try passing the user who posted a bid on a listing through a form. I already asked this about passing the creator of a listing and I tried the same method but I cannot manage to do it.
My view looks something like this (I shortened it because it's very long):
def show_listing(request, listing_id):
listing = AuctionListing.objects.get(id=listing_id)
bidding = listing.bidlisting.last()
if bidding is None:
field_name = "starting_bid"
starting_bid = getattr(listing, field_name)
createbidform = CreateBid(initial={"bids": starting_bid, "user":request.user})
else:
field_name2 = "bids"
highest_bid = getattr(bidding, field_name2)
createbidform = CreateBid(initial={"bids": highest_bid, "user":request.user})
if request.method == "POST":
form = CreateBid(request.POST)
if bidding is None and float(form['bids'].value()) >= float(starting_bid):
if form.is_valid():
message = "Your bid is placed"
form.instance.listing = listing
form.save()
createbidform = CreateBid(initial={"bids": form['bids'].value(), "user":request.user})
amount_bids = len(Bid.objects.filter(listing=listing_id))
return render(request, "auctions/listing.html", {
"createbidform" : createbidform
})
else:
print(form.errors)
return render(request, "auctions/listing.html", {
"bidform" : createbidform
})
listing.html looks something like this:
<form method="POST">
{% csrf_token %}
${{ bidform.bids }}
<button type="submit" name="bidbid" class="btn btn-primary save btn-sm">Place your bid</button>
</form>
RIGHT NOW form.errors prints:
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Here is the model Bid:
class Bid(models.Model):
listing = models.ForeignKey(AuctionListing, on_delete=models.CASCADE, related_name="bidlisting")
bids = models.DecimalField(max_digits=6, decimal_places=2)
user = models.ForeignKey(User, on_delete=models.CASCADE, db_constraint=False, related_name="userrr")
def __str__(self):
return str(self.bids)
And here is the form CreateBid:
class CreateBid(forms.ModelForm):
class Meta:
model = Bid
fields = ('bids', 'user')
widgets = {
'user': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super().__init__(*args, **kwargs)
self.fields['user'].initial = user.id
For some reason it doesn't provide the user who posted the bidding to the form, causing the form to not be valid. How to fix this?
You already do it with bids so you don't need extra kwarg:
createbidform = CreateBid(initial={'bids': starting_bid, 'user': request.user})
you can remove __init__ method form CreateBid form class
Template:
<form method="POST">
{% csrf_token %}
${{ bidform }}
<button type="submit" name="bidbid" class="btn btn-primary save btn-sm">Place your bid</button>
</form>

Foreign key is not assigned to a model in database

I recently started learning django and was making a CRM.
models.py:
class Complaint(models.Model):
SOURCE_CHOICES=(
('email','E-mail'),
('call','Call'),
('ticket','Ticket')
)
store_name=models.CharField(max_length=20)
store_contact_no=models.IntegerField(max_length=10)
store_id=models.CharField(max_length=7)
source=models.CharField(choices=SOURCE_CHOICES, max_length=10)
agent=models.ForeignKey("Agent", null = True, blank = True, on_delete=models.SET_NULL)
category=models.ForeignKey("Category", related_name="complaints", null = True, blank = True, on_delete=models.SET_NULL)
description = models.TextField()
customer = models.ForeignKey("Customer", null = True, blank = True, on_delete=models.SET_NULL)
def __str__(self):
return f"{self.store_name} {self.store_id}"
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
forms.py
class CustomerComplaintForm(forms.ModelForm):
class Meta:
model = Complaint
fields = (
'store_name',
'store_id',
'store_contact_no',
'description',
)
views.py
class
CustomerComplaintCreateView(OwnerCustomerAndLoginRequiredMixin,
generic.CreateView):
template_name = "customercomplaint_create.html"
form_class = CustomerComplaintForm
def get_success_url(self):
return "/complaints"
def form_valid(self, form):
complaint = form.save(commit=False)
complaint.Customer = self.request.user.username
complaint.source = 'ticket'
complaint.save()
return super(CustomerComplaintCreateView,
self).form_valid(form)
html template:
{% extends "base.html" %}
{% load tailwind_filters %}
{% block content %}
<div class="max-w-lg mx-auto">
<a class="hover:text-blue-500" href="/complaints">Go back to complaints </a>
<div class="py-5 border-t border-gray-200">
<h1 class="text-4xl text-gray-800"> Create a new complaint </h1>
</div>
<form method='post' class="mt-5">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="w-full bg-blue-500 text-white hover:bg-blue-600 px-
3 py-2 rounded-md">Create</button>
</form>
</div>
{% endblock content %}
mixins.py
class OwnerCustomerAndLoginRequiredMixin(AccessMixin):
"""Verify that the current user is authenticated and is an owner or customer"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_owner and not request.user.is_customer:
return redirect("/complaints")
return super().dispatch(request, *args, **kwargs)
The problem here is that, the source field gets filled in the database with Ticket as intended. But the 'Customer' field is not populated with the username. 'Self.request.user.username' is not the problem here as the username is being printed correctly in the console.
The issue is complaint.Customer = self.request.user.username, you're trying to assign a username to a supposedly Customer object. Here's an approach you could take to solve the issue though.
Within the views.py file, the view class.
You could get the customer object and then assign it to the customer field on the complaint object.
from django.shortcuts import get_object_or_404
def form_valid(self, form):
complaint = form.save(commit=False)
customer = get_object_or_404(Customer, user=self.request.user) # recommended
# or
# customer = get_object_or_404(Customer, user__username__iexact=self.request.user.username)
if customer:
# Here on your model you have a lowercase `c` for the customer field, not 'C`
complaint.customer = customer # -> This will assign the customer object, "FK".
complaint.source = 'ticket'
complaint.save()
return super(CustomerComplaintCreateView, self).form_valid(form)
That should work.
this must be the User not the user name
Because Cutomer is User object not only the Uesrname
def form_valid(self, form):
complaint = form.save(commit=False)
complaint.Customer = self.request.user
complaint.source = 'ticket'
complaint.save()

Datalist with free text error "Select a valid choice. That choice is not one of the available choices."

I am building a Create a Recipe form using crispy forms and I am trying to use a datalist input field for users to enter their own ingredients, like 'Big Tomato' or select from GlobalIngredients already in the database like 'tomato' or 'chicken'. However, regardless of whether I enter a new ingredient or select a pre-existing one, I am getting the following error: "Select a valid choice. That choice is not one of the available choices.". How do I fix this error?
Visual:
models.py
class Recipe(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
websiteURL = models.CharField(max_length=200, blank=True, null=True)
image = models.ImageField(upload_to='image/', blank=True, null=True)
name = models.CharField(max_length=220) # grilled chicken pasta
description = models.TextField(blank=True, null=True)
notes = models.TextField(blank=True, null=True)
serves = models.CharField(max_length=30, blank=True, null=True)
prepTime = models.CharField(max_length=50, blank=True, null=True)
cookTime = models.CharField(max_length=50, blank=True, null=True)
class Ingredient(models.Model):
name = models.CharField(max_length=220)
def __str__(self):
return self.name
class GlobalIngredient(Ingredient):
pass # pre-populated ingredients e.g. salt, sugar, flour, tomato
class UserCreatedIngredient(Ingredient): # ingredients user adds, e.g. Big Tomatoes
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class RecipeIngredient(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, null=True, on_delete=models.SET_NULL)
description = models.TextField(blank=True, null=True)
quantity = models.CharField(max_length=50, blank=True, null=True) # 400
unit = models.CharField(max_length=50, blank=True, null=True) # pounds, lbs, oz ,grams, etc
forms.py
class RecipeIngredientForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RecipeIngredientForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
#self.helper.form_id = 'id-entryform'
#self.helper.form_class = 'form-inline'
self.helper.layout = Layout(
Div(
Div(Field("ingredient", placeholder="Chickpeas - only write the ingredient here"), css_class='col-6 col-lg-4'),
Div(Field("quantity", placeholder="2 x 400"), css_class='col-6 col-md-4'),
Div(Field("unit", placeholder="grams"), css_class='col-5 col-md-4'),
Div(Field("description", placeholder="No added salt tins - All other information, chopped, diced, whisked!", rows='3'), css_class='col-12'),
css_class="row",
),
)
class Meta:
model = RecipeIngredient
fields = ['ingredient', 'quantity', 'unit', 'description']
labels = {
'ingredient': "Ingredient",
"quantity:": "Ingredient Quantity",
"unit": "Unit",
"description:": "Ingredient Description"}
widgets={'ingredient': forms.TextInput(attrs={
'class': 'dropdown',
'list' : 'master_ingredients',
'placeholder': "Chickpeas - only write the ingredient here"
})}
views.py
#login_required
def recipe_create_view(request):
ingredient_list = Ingredient.objects.all()
form = RecipeForm(request.POST or None)
# Formset = modelformset_factory(Model, form=ModelForm, extra=0)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
RecipeInstructionsFormset = formset_factory(RecipeInstructionForm, extra=0)
instructionFormset = RecipeInstructionsFormset(request.POST or None, initial=[{'stepName': "Step 1"}], prefix="instruction")
context = {
"form": form,
"formset": formset,
"instructionFormset": instructionFormset,
"ingredient_list": ingredient_list
}
if request.method == "POST":
print(request.POST)
if form.is_valid() and formset.is_valid() and instructionFormset.is_valid():
parent = form.save(commit=False)
parent.user = request.user
parent.save()
# formset.save()
#recipe ingredients
for form in formset:
child = form.save(commit=False)
print(child.ingredient)
globalIngredient = Ingredient.objects.filter(name=child.ingredient.lower()) # not truly global as this will return user ingredients too
if (globalIngredient):
pass
else:
newIngredient = UserCreatedIngredient(user=request.user, name=child.ingredient.lower())
newIngredient.save()
if form.instance.ingredient.strip() == '':
pass
else:
child.recipe = parent
child.save()
# recipe instructions
for instructionForm in instructionFormset:
instructionChild = instructionForm.save(commit=False)
if instructionForm.instance.instructions.strip() == '':
pass
else:
instructionChild.recipe = parent
instructionChild.save()
context['message'] = 'Data saved.'
return redirect(parent.get_absolute_url())
else:
form = RecipeForm(request.POST or None)
formset = RecipeIngredientFormset()
instructionFormset = RecipeInstructionsFormset()
return render(request, "recipes/create.html", context)
create.html
<!--RECIPE INGREDIENTS-->
{% if formset %}
<h3 class="mt-4 mb-3">Ingredients</h3>
{{ formset.management_form|crispy }}
<div id='ingredient-form-list'>
{% for ingredient in formset %}
<div class='ingredient-form'>
{% crispy ingredient %}
</div>
{% endfor %}
<datalist id="master_ingredients">
{% for k in ingredient_list %}
<option value="{{k.name|title}}"></option>
{% endfor %}
</datalist>
</div>
<div id='empty-form' class='hidden'>
<div class="row mt-4">
<div class="col-6">{{ formset.empty_form.ingredient|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.quantity|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.unit|as_crispy_field }}</div>
<div id="ingredientIdForChanging" style="display: none;"><div class="col-12">{{ formset.empty_form.description|as_crispy_field }}</div><button type="button"
class="btn btn-outline-danger my-2" onclick="myFunction('showDescription')"><i class="bi bi-dash-circle"></i> Hide
Description</button></div><button type="button"
class="btn btn-outline-primary col-5 col-md-3 col-lg-3 col-xl-3 m-2" id="ingredientIdForChanging1"
onclick="myFunction('showDescription')"><i class="bi bi-plus-circle"></i> Add a
Description Field</button>
</div>
</div>
<button class="btn btn-success my-2" id='add-more' type='button'>Add more ingredients</button>
{% endif %}
You can create your own TextInput and TypedModelListField field to handle this. I think what you're looking for is something which allows the user to both search and provide a recommended selection of choices but validate the input against a model (Ingredient).
I've created one here:
class TypedModelListField(forms.ModelChoiceField):
def to_python(self, value):
if self.required:
if value == '' or value == None:
raise forms.ValidationError('Cannot be empty')
validate_dict = {self.validate_field: value}
try:
value = type(self.queryset[0]).objects.get(**validate_dict))
except:
raise forms.ValidationError('Select a valid choice. That choice is not one of the available choices.')
value = super().to_python(value)
return value
def __init__(self, *args, **kwargs):
self.validate_field= kwargs.pop('validate_field', None)
super().__init__(*args, **kwargs)
class ListTextWidget(forms.TextInput):
def __init__(self, dataset, name, *args, **kwargs):
super().__init__(*args)
self._name = name
self._list = dataset
self.attrs.update({'list':'list__%s' % self._name,'style': 'width:100px;'})
if 'width' in kwargs:
width = kwargs['width']
self.attrs.update({'style': 'width:{}px;'.format(width)})
if 'identifier' in kwargs:
self.attrs.update({'id':kwargs['identifier']})
def render(self, name, value, attrs=None, renderer=None):
text_html = super().render(name, value, attrs=attrs)
data_list = '<datalist id="list__%s">' % self._name
for item in self._list:
data_list += '<option value="%s">' % item
data_list += '</datalist>'
return (text_html + data_list)
Within the RecipeIngredientForm add the following definition:
ingredient = TypedModelListField(
queryset=Ingredient.objects.all(),
validate_field='name')
And then in RecipeIngredientForm within the __init__ function. Include the following after the super() is called.
self.fields['ingredient'].widget = ListTextWidget(
dataset=Ingredient.objects.all(),
name='ingredient_list')
With ecogels comment I was able to understand what was causing the issue and with a combination of Lewis answer and this answer I managed to get this working with the following code.
fields.py
from django import forms
class ListTextWidget(forms.TextInput):
def __init__(self, data_list, name, *args, **kwargs):
super(ListTextWidget, self).__init__(*args, **kwargs)
self._name = name
self._list = data_list
self.attrs.update({'list':'list__%s' % self._name})
def render(self, name, value, attrs=None, renderer=None):
text_html = super(ListTextWidget, self).render(name, value, attrs=attrs)
data_list = '<datalist id="list__%s">' % self._name
for item in self._list:
data_list += '<option value="%s">' % str(item).title()
data_list += '</datalist>'
return (text_html + data_list)
forms.py
from .fields import ListTextWidget
class RecipeIngredientForm(forms.ModelForm):
ingredientName = forms.CharField(required=True)
def __init__(self, *args, **kwargs):
super(RecipeIngredientForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Div(
Div(Field("ingredientName", placeholder="Chickpeas - only write the ingredient here"), css_class='col-6 col-lg-4'),
Div(Field("quantity", placeholder="2 x 400"), css_class='col-6 col-md-4'),
Div(Field("unit", placeholder="grams"), css_class='col-5 col-md-4'),
Div(Field("description", placeholder="No added salt tins - All other information, chopped, diced, whisked!", rows='3'), css_class='col-12'),
css_class="row",
),
)
self.fields['ingredientName'].widget = ListTextWidget(data_list=Ingredient.objects.all(), name='ingredient-list')
class Meta:
model = RecipeIngredient
fields = ['ingredientName', 'quantity', 'unit', 'description']
labels = {
'ingredientName': "Ingredient",
"quantity:": "Ingredient Quantity",
"unit": "Unit",
"description:": "Ingredient Description"}
create.html:
<!--RECIPE INGREDIENTS-->
{% if formset %}
<h3 class="mt-4 mb-3">Ingredients</h3>
{{ formset.management_form|crispy }}
<div id='ingredient-form-list'>
{% for ingredient in formset %}
<div class='ingredient-form'>
{% crispy ingredient %}
</div>
{% endfor %}
</div>
<div id='empty-form' class='hidden'>
<div class="row mt-4">
<div class="col-6">{{ formset.empty_form.ingredientName|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.quantity|as_crispy_field }}</div>
<div class="col-6">{{ formset.empty_form.unit|as_crispy_field }}</div>
<div id="ingredientIdForChanging" style="display: none;"><div class="col-12">{{ formset.empty_form.description|as_crispy_field }}</div><button type="button"
class="btn btn-outline-danger my-2" onclick="myFunction('showDescription')"><i class="bi bi-dash-circle"></i> Hide
Description</button></div><button type="button"
class="btn btn-outline-primary col-5 col-md-3 col-lg-3 col-xl-3 m-2" id="ingredientIdForChanging1"
onclick="myFunction('showDescription')"><i class="bi bi-plus-circle"></i> Add a
Description Field</button>
</div>
</div>
<button class="btn btn-success my-2" id='add-more' type='button'>Add more ingredients</button>
{% endif %}
views.py changes:
form = RecipeForm(request.POST or None)
# Formset = modelformset_factory(Model, form=ModelForm, extra=0)
RecipeIngredientFormset = formset_factory(RecipeIngredientForm)
formset = RecipeIngredientFormset(request.POST or None)
RecipeInstructionsFormset = formset_factory(RecipeInstructionForm, extra=0)
instructionFormset = RecipeInstructionsFormset(request.POST or None, initial=[{'stepName': "Step 1"}], prefix="instruction")
URLForm = RecipeIngredientURLForm(request.POST or None)
context = {
"form": form,
"formset": formset,
"URLForm": URLForm,
"instructionFormset": instructionFormset
}

ValueError: The view videos.views.add_comment_post didn't return an HttpResponse object. It returned None instead in Django

I tried to add comments with the post and it raise this error, and I supmit my comment using ajax but it seems the problem coming from the view but I couldn't figure out what exactly the problem
My add comments View
#login_required
def add_comment_post(request):
comment_form = PostCommentForm(request.POST)
if comment_form.is_valid():
user_comment = comment_form.save(commit=False)
user_comment.author = request.user
user_comment.save()
result = comment_form.cleaned_data.get('content')
user = request.user.username
return JsonResponse({'result': result, 'user': user})
My comment Model
class PostCommentIDF(MPTTModel):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='pos_com')
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='post_children')
author = models.ForeignKey(Account, on_delete=models.CASCADE)
content = models.TextField()
publish = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=True)
likes = models.ManyToManyField(Account, blank=True, related_name='pos_com')
class MPTTMeta:
order_insertion_by = ['-publish']
def __str__(self):
return f'{self.author}---{self.content[:15]}'
My form for the comments
class PostCommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class Meta:
model = PostCommentIDF
fields = {'post', 'content'}
widgets = {
'content': forms.Textarea(attrs={'class': 'rounded-0 form-control', 'rows': '1', 'placeholder': 'Comment', 'required': 'True'})
}
def save(self, *args, **kwargs):
PostCommentIDF.objects.rebuild()
return super(PostCommentForm, self).save(*args, **kwargs)
the comments form in the template
<form id="Postcommentform" class="Postcommentform" method="post" style="width: 100%;">
{% load mptt_tags %}
{% csrf_token %}
<select class="d-none" name="video" id="id_video">
<option value="{{ video.id }}" selected="{{ video.id }}"></option>
</select>
<div class="d-flex">
<label class="small font-weight-bold">{{ comments.parent.label }}</label>
{{ comment_form.parent }}
{{comments.content}}
<button value="Postcommentform" id="Postnewcomment" type="submit" style="color: white; border-radius: 0;" class="d-flex justify-content-end btn btn-primary">Post</button>
</div>
</form>
When form is not valid your the codes under your if statement will not execute so your function will return None instead of an HttpResponse.
You should handle it with in a esle statement for example:
#login_required
def add_comment_post(request):
comment_form = PostCommentForm(request.POST)
if comment_form.is_valid():
user_comment = comment_form.save(commit=False)
user_comment.author = request.user
user_comment.save()
result = comment_form.cleaned_data.get('content')
user = request.user.username
return JsonResponse({'result': result, 'user': user})
else:
return JsonResponse({'result': "form is not valid"})

image upload in a template

My issue is if I upload from admin an image it works fine but if I want to add an image from my template, it seems that my image does not updload.
It is weird as in the request.Post form, path of my image is present.
I've no message of error...
this is my model:
class Listing(models.Model):
title = models.CharField('Title',max_length=64, blank=False)
description = models.TextField('Descritpion', blank=False)
Creation_date = models.DateField(auto_now_add=True)
enddate= models.DateField('Ending Date', blank=False)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category")
initialBid = models.DecimalField('Bid', max_digits=12, decimal_places=2, blank=False)
photo = ResizedImageField(size=[300, 150], upload_to='images/', default='images/default.jpg')
active = models.BooleanField('Active', default=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="author")
def __str__(self):
return f"{self.id} : {self.title} {self.initialBid} {self.Creation_date}"
def save(self, *args, **kwargs):
# Check how the current ending date is after created date,
d1 = self.Creation_date
d2 = self.enddate
if d2 <= d1:
raise ValueError("End date must be a day after today")
super(Listing, self).save(*args, **kwargs)
my form:
class NewListingForm(forms.ModelForm):
enddate = forms.DateField(label='Date of auction End', widget=forms.DateInput(format = '%d/%m/%Y'),
input_formats=('%d/%m/%Y',))
def __init__(self, *args, **kwargs):
self._newly_created = kwargs.get('instance') is None
self.Creation_date = datetime.now()
super().__init__(*args, **kwargs)
class Meta:
model = Listing
fields = ('title','description','enddate','category','initialBid','photo','active')
my template:
<h2>{% if not form.instance.pk %}Create listing {% else %} Edit {% endif %}</h2>
<form id="create-edit-client" class="form-horizontal" action="" method="post" accept-charset="utf-8" enctype="multipart/form-data">
{% csrf_token %}
{{ form.title|as_crispy_field }}
{{ form.description|as_crispy_field }}
<br>
<div class="row">
<div class="col-3">
{{ form.category|as_crispy_field }}
</div>
<div class="col-2">
{{ form.initialBid|as_crispy_field }}
</div>
</div>
<br>
<div class="row">
<div class="col-3">
{{ form.enddate|as_crispy_field }}
</div>
<div class="col-2">
{{ form.active|as_crispy_field }}
</div>
</div>
<br>
{{ form.photo|as_crispy_field }}
<br>
<br>
</form>
in settings:
STATIC_URL = '/static/'
# gestion ds media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
in urls:
urlpatterns = [
......
]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
my views:
#login_required
def editlisting(request, listing_id):
obj = Listing.objects.filter(pk=listing_id).first()
if obj is None:
return render(request, "errors/404.html", {
"entryTitle": listing_id
})
else:
form = NewListingForm(instance=Listing.objects.get(pk=listing_id))
modification=True # used in template to check if modification are allowed
# read only form if not author
user = request.user.id
if obj.author.id != user:
modification=False
# manage save
if request.method == "POST":
form = NewListingForm(request.POST, instance=Listing.objects.get(pk=listing_id))
if form.is_valid():
form.save()
messages.success(request, 'listing saved') # message for inform user of success - See messages in html file
return HttpResponseRedirect(reverse("index"))
# check if in wishes on get
wishbool=False
wish = Wishing.objects.filter(item=listing_id, follower=request.user.id).first()
if wish is not None:
wishbool = True
return render(request, 'auctions/newListing.html', {
"form": form,
"existing": True,
'title': "Edit Listing",
"wishing": wishbool,
"modification":modification
})
thanks for help
form = NewListingForm(request.POST, request.FILES,instance=Listing.objects.get(pk=listing_id))
for getting the images or files you need pass another request that it's called request.FILES, so:
#login_required
def editlisting(request, listing_id):
obj = Listing.objects.filter(pk=listing_id).first()
if obj is None:
return render(request, "errors/404.html", {
"entryTitle": listing_id
})
else:
form = NewListingForm(instance=Listing.objects.get(pk=listing_id))
modification=True # used in template to check if modification are
allowed
# read only form if not author
user = request.user.id
if obj.author.id != user:
modification=False
# manage save
if request.method == "POST":
form = NewListingForm(request.POST, request.FILES,
instance=Listing.objects.get(pk=listing_id))
if form.is_valid():
form.save()
messages.success(request, 'listing saved') # message for
inform user of success - See messages in html file
return HttpResponseRedirect(reverse("index"))
# check if in wishes on get
wishbool=False
wish = Wishing.objects.filter(item=listing_id,
follower=request.user.id).first()
if wish is not None:
wishbool = True
return render(request, 'auctions/newListing.html', {
"form": form,
"existing": True,
'title': "Edit Listing",
"wishing": wishbool,
"modification":modification
})