image upload in a template - django

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

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>

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

Why does form.is_valid return false. Please advice

I have a project where one books a room online then is directed to the checkout page where you input a phone which is required in the backend. Code is as below:
forms.py
from django import forms
class AvailabilityForm(forms.Form):
check_in = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M'], required=True)
check_out = forms.DateTimeField(input_formats=['%Y-%m-%dT%H:%M'], required=True)
class PhoneNoForm(forms.Form):
phone_no = forms.IntegerField(required=True)
views.py
class RoomDetailView(View):
def get(self, request, *args, **kwargs):
category = self.kwargs.get('category', None)
got_category = get_room_category(category)
print(category)
print(got_category)
form = AvailabilityForm()
if got_category is not None:
context = {
'category':got_category,
'form':form
}
return render(request, 'detail.html', context)
else:
return HttpResponse("Category Nyet!")
def post(self, request, *args, **kwargs):
category = self.kwargs.get('category', None)
form = AvailabilityForm(request.POST)
if form.is_valid():
data = form.cleaned_data
available_rooms = get_available_rooms(category, data['check_in'], data['check_out'] )
if available_rooms is not None:
room = available_rooms[0]
context = {
'room':room
}
return render(request, 'booking/checkout.html', context)
else:
return HttpResponse("We're out of those rooms" )
else:
return HttpResponse('Form invlid')
class CheckoutView(TemplateView):
template_name = 'booking/checkout.html'
def get_phone(request):
if request.POST:
phone_no = request.POST.get('PhoneNo', None)
print(phone_no)
cl = MpesaClient()
# Use a Safaricom phone number that you have access to, for you to be able to view the prompt
phone_number = phone_no
amount = 1
account_reference = 'reference'
transaction_desc = 'Description'
callback_url = 'https://darajambili.herokuapp.com/express-payment'
# callback_url = request.build_absolute_uri(reverse('booking:mpesa_stk_push_callback'))
response = cl.stk_push(phone_number, amount, account_reference, transaction_desc, callback_url)
return HttpResponse(response.text)
else:
return HttpResponse('This is else')
checkout.html
<div>
<form action="" method="POST">
{% csrf_token %}
<label for="Input No.">Input Phone No. :</label>
<input type="number" name="id_PhoneNo" id="PhoneNo">
<input type="submit" value="Proceed">
</form>
</div>
detail.html
<form id="booking-form" action="" method="POST">
{% csrf_token %}
<div class="input-div">
<label for="id_check_in">Check In : </label>
<input type="datetime-local" id="id_check_in" name="check_in">
</div>
<div class="input-div">
<label for="id_check_out">Check Out : </label>
<input type="datetime-local" id="id_check_out" name="check_out">
</div>
<div class="input-div">
<button type="submit">Book the Room</button>
</div>
</form>
form.is_valid returns False. I have tried using GET method but i need the phone number for more processing hence my insistence on using POST. If possible, help me fast. Where is the problem? Sorry if it's too much code.
You need to return the errors to the HTML pages to see why the error is occurring. So, if the form is not valid, send that to HTML page by:
def post(self, request, *args, **kwargs):
category = self.kwargs.get('category', None)
form = AvailabilityForm(request.POST)
if form.is_valid():
...
else:
category = self.kwargs.get('category', None)
got_category = get_room_category(category)
context = {
'category':got_category,
'form':form
}
return render(request, 'detail.html', context)
And render the errors like this(as per documentation):
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
FYI, rather than directly subclassing the View, you should use FormView. Here is an example code on how to use:
class RoomDetailView(FormView):
form_class = AvailabilityForm
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
category = self.kwargs.get('category', None)
got_category = get_room_category(category)
if got_category is not None:
context.update({
'category':got_category,
})
return context
else:
raise Http404("Category does not exist")
def form_valid(self, form):
data = form.cleaned_data
available_rooms = get_available_rooms(category, data['check_in'], data['check_out'] )
if available_rooms is not None: # this part should be in a separate view
room = available_rooms[0]
context = {
'room':room
}
return render(request, 'booking/checkout.html', context)
else:
return HttpResponse("We're out of those rooms" )

Django Formset image.url Not Populated

I have an image field on a model that i'd like to render inside an img tag. It's inside of a formset and I can see the link to the image, but if I try to reference form.image.url nothing comes back. form.image returns "Current :" "" which breaks because of the leading string. How can get the images to display?
Stack
Django 2.1, django-storages using S3
Storages Settings
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_DEFAULT_ACL = 'None'
AWS_S3_REGION_NAME = "us-east-1"
AWS_LOCATION = "images"
AWS_STORAGE_BUCKET_NAME = 'fakebucketname'
MEDIA_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME
AWS_S3_HOST = 's3.us-east-1.amazonaws.com'
AWS_DEFAULT_ACL = None
Models
class Note(models.Model):
user = models.CharField(null=True, max_length=25)
image = models.ImageField(upload_to="%Y/%m/%d/")
text = models.TextField(verbose_name='Text', null=True)
pub_date = models.DateTimeField(auto_now_add=True)
report = models.ForeignKey(Report, on_delete=models.CASCADE, null=True)
def __str__(self):
return self.user
class Report(models.Model):
name = models.DateField(auto_now_add=True, unique=True, verbose_name='Report Name')
slug = models.SlugField(blank=True)
def __str__(self):
return str(self.name)
def __unicode__(self):
return u"%s" % self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
return super(Report, self).save(*args, **kwargs)
def get_absolute_url(self):
from django.urls import reverse
return reverse('report_detail', args=[str(self.id)])
Forms
from TheDaily.models import Report, Note
from django.forms import ModelForm, inlineformset_factory
class NoteForm(ModelForm):
def __init__(self, *args, **kwargs):
super(NoteForm, self).__init__(*args, **kwargs)
self.fields['image'].required = False
class Meta:
model = Note
exclude = ()
class ReportForm(ModelForm):
class Meta:
model = Report
fields = '__all__'
NoteFormset = inlineformset_factory(Report, Note, form=NoteForm, extra=1)
View
def manage_reports(request, pk):
class Media(object):
js = formset_media_js + (
)
report = Report.objects.get(pk=pk)
note_inline_form_set = inlineformset_factory(Report, Note, extra=2,
fields=('user', 'category', 'text', 'report', 'image'),
widgets={'text': forms.Textarea(attrs={'class': 'form-control'}),
'user': forms.TextInput(attrs={'class': 'form-control'}),
'image': forms.ClearableFileInput,
'category': forms.Select(attrs={'class': 'form-control'})})
if request.method == "POST":
formset = note_inline_form_set(request.POST, request.FILES, instance=report)
if formset.is_valid():
for form in formset:
form.cleaned_data['user'] = request.user.username
formset.save()
return HttpResponseRedirect('/')
else:
formset = note_inline_form_set(instance=report)
return render(request, 'daily/report.html', {'formset': formset})
Template
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{% load formset_tags %}
<div id="formset" data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
<div data-formset-body>
<!-- New forms will be inserted in here -->
{% for form in formset %}
<div data-formset-form>
{{ form.as_p }}
<img id="image_preview"src="{{ form.image.url }}"/>
</div>
{% endfor %}
</div>
<!-- The empty form template. By wrapping this in a <script> tag, the
__prefix__ placeholder can easily be replaced in both attributes and
any scripts -->
<script type="form-template" data-formset-empty-form enctype="multipart/form-data">
{% escapescript %}
<div data-formset-form>
{{ formset.empty_form }}
</div>
{% endescapescript %}
</script>
<!-- This button will add a new form when clicked -->
<input type="button" value="Add another" data-formset-add class="btn btn-w-md btn-info">
<input type="submit" value="Submit" class="btn btn-w-md btn-accent"/>
<script>jQuery(function ($) {
$("#formset").formset({
animateForms: true
});
});</script>
</div>
</form>
Django ModelForm ImageField
I went with the 2nd option and made the NonClearableImageInput widget. Seems to be working well

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