Django - User not passing through form - django

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>

Related

Django - Passing the creator of a form to the form

I am making an auction site. I need to know per listing who the creator was (so the creators will have the possibility to delete the listing). It works for me to manually change the user in Django Admin, but I want it to be automatically saved when someone creates a new listing.
How do I pass the creator of a form to the form?
These are the relevant models:
class User(AbstractUser):
pass
class AuctionListing(models.Model):
title = models.CharField(max_length=64)
description = models.CharField(max_length=512, default="")
starting_bid = models.DecimalField(max_digits=6, decimal_places=2, default=0.01, validators=[MinValueValidator(Decimal('0.01'))])
url = models.URLField(max_length=200, blank=True)
category = models.CharField(max_length = 20)
user = models.ForeignKey(User, on_delete=models.CASCADE, db_constraint=False, related_name="items")
def __str__(self):
return self.title
Here is my forms.py:
class CreateListing(forms.ModelForm):
category = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label="No category")
class Meta:
model = AuctionListing
fields = ('title', 'description', 'starting_bid', 'url', 'category', 'user')
widgets = {
'title': forms.TextInput(attrs={'placeholder':'Write your listing title here...'}),
'description': forms.Textarea(attrs={'placeholder':'Write your comment here...', 'rows':3}),
'user': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super().__init__(*args, **kwargs)
self.fields['user'].initial = user.id
And here was my attempt for the view:
def create_listing(request):
form = CreateListing(request.POST, user=request.user)
if request.method == "POST":
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("index"))
else:
print("RIP")
return render(request, "auctions/create_listing.html", {"form": form})
return render(request, "auctions/create_listing.html", {
"form" : CreateListing(user=request.user)
})
auctions/create_listing.html looks like this:
<h1> Create new listing </h1>
<form action="" method="POST">
{% csrf_token %}
<label>Name*</label>
<br>
{{ form.title }}
<br>
<label >Description*</label>
<br>
{{ form.description }}
<br>
<label >Starting bid*</label>
<br>
{{ form.starting_bid }}
<br>
<label > Image url (optional) </label>
<br>
{{ form.url }}
<br>
<label > Category (optional) </label>
<br>
{{ form.category }}
<br>
{{ form.user }}
<button type="submit" class="btn btn-primary save">Create your listing</button>
</form>
The error I get with this is: "BaseModelForm.init() got an unexpected keyword argument 'user'"
How can I fix this so the user will automatically be saved each time a listing is created?
Add user argument in the __init__ method of the form. You can set the user right there and there is no need for even displaying the user field. You can completely hide it.
class CreateListing(forms.ModelForm):
category = forms.ModelChoiceField(queryset=Category.objects.all(), empty_label="No category")
class Meta:
model = AuctionListing
fields = ('title', 'description', 'starting_bid', 'url', 'category', 'user')
widgets = {
'title': forms.TextInput(attrs={'placeholder':'Write your listing title here...'}),
'description': forms.Textarea(attrs={'placeholder':'Write your comment here...', 'rows':3}),
'user': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super().__init__(*args, **kwargs)
self.fields['user'].initial = user.id
Remember to use the user kwarg in the view:
def create_listing(request):
form = CreateListing(request.POST, user=request.user)
if request.method == "POST":
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse("index"))
else:
print("RIP")
# If form is invalid you should render the same template again
# with the errors
return render(request, "auctions/create_listing.html", {"form": form})
return render(request, "auctions/create_listing.html", {
# For the empty form, this needs to be an instance of the form,
# and not a class
"form" : CreateListing(user=request.user)
})
Also, you can replace your HTML with:
{{ form.user }}
Replace {{ form.user }} by <input type="hidden" name="user" value="{{request.user}}">
User has to come automatically so you don't need to display user field in the front-end
by the above method you'll get logged in user but still hard to save as it is foreign key, what ever you get from front-end it'll be string type.
So best suggestion is the below code.
if form.is_valid():
form_data = form.save()
form_data.user = request.user
form_data.save()

Create recommendation system from values in Django template

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.

Django, Foreign key not getting filled with session data

im trying to fill my foreignkey (employer) with the user that is logged in, but i have seen alot of way but they havent worked for me, does anyone know what im doing wrong? and how i can fix it?
View:
class JobCreate(CreateView):
model = Job
form = JobCreateForm()
form_class = JobCreateForm
context = {}
success_url = reverse_lazy('jobsview')
def POST(self,request):
if request.method == 'POST':
form = JobCreateForm(request.POST)
if form.is_valid():
job = form.save(commit=False)
job.employer = request.user
job.save()
context = {}
return render(request, 'jobs/jobs.html',context)
else:
context = {}
return render(request, 'jobs/job_form.html',context)
Model:
class Job(models.Model):
employer = models.ForeignKey(User, related_name='employer', on_delete=CASCADE,blank=True)
employees = models.ManyToManyField(User, related_name='employees2user',null=True,blank=True)
title = models.CharField(max_length=200,)
description = models.CharField(max_length=200,null=True,blank=True)
category_id = models.ManyToManyField(Category,blank=True)
skill_id = models.ManyToManyField(Skill,blank=True)
approved = models.BooleanField(default=False)
# img = models.ImageField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): # Default value
return self.title
HTML:
{% extends "jobs/layout.html" %}
{% block content %}
<h3> Job</h3>
<div class="container">
<div class="jobform">
<form action="" method="POST">
{%csrf_token%}
{% for field in form %}
<label for="{{field.id_for_label}}">{{field.html_name}}</label>
{{field}}
{% endfor %}
<p>Ctrl in houden om meerder te selecteren</p>
<button type="submit" class="btn btn-dark btn-space">Submit</button>
</form>
</div>
</div>
{%endblock%}
In your views method, try replacing
job = form.save(commit=False)
job.employer = request.user
job.save()
with
self.object = form.save(commit=False)
self.object.employer = self.request.user
self.object.save()
Also, what is the purpose of context {} ?
Can you just put this inside a form_valid method directly? That's much cleaner.
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.employer = self.request.user
self.object.save()
return super(JobCreate, self).form_valid(form)

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
})

Django - Modelform not rendering

I created a form to update a User's profile, however when I run it, there are no errors, but when I try to open up the page, the header appears but the UpdateBioForm does not appear. Secondly, I was wondering how you would create a large textbox to store someone's biography.
Models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
biography = models.CharField(max_length = 255, default = '')
city = models.CharField(max_length=100, default = '')
website = models.URLField(default='')
image = models.ImageField(upload_to='profile_image', blank=True)
def setdefault(self, default_path='/profile_image/Default.jpg'):
if self.image:
return self.image
return default_path
def __str__(self):
return self.user.username
Forms.Py
class UpdateBioForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = (
'biography',
'city',
'website'
)
def save(self, commit=True):
savedBio = super(UpdateBioForm, self).save(commit=False)
savedBio.biography = self.cleaned_data['biography']
savedBio.city = self.cleaned_data['city']
savedBio.website = self.cleaned_data['website']
if commit:
savedBio.save()
return savedBio
Views.py
def update_bio(request):
if request.method == 'POST':
form = UpdateBioForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect('/')
else:
form = UpdateBioForm(instance=request.user)
args = {'form':form}
return render(request, 'accounts/update_bio.html')
urls.py
url(r'^profile/updatebio/$',views.update_bio, name='update_bio'),
update_bio.html
{% extends 'base.html' %}
{% block body %}
<div class="container">
<h1>Update Biography</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</div>
{% endblock %}
You are not passing any context to your render() method - you define args but don't do anything with that variable. Change it to:
args = {'form':form}
return render(request, 'accounts/update_bio.html', context=args) # <-- You're missing context