I created a formset that has maximum of 5 images to be attached;
1 - but I want the Validation to run only when the user has not attached any image(ValidationError('atleast 1 image is required')),
2- This program is also not allowing the User to save when 1, or 2, or 3 images are attached, which I really need. So if there is 1 image or 2, that should be allowed to save.
3 - I also need to make the 1 radio-button to be selected by default, to make the selected image to be the one dispalyed in the template
template
<form enctype="multipart/form-data" action="" method="post"> {% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{{ form.as_p }}
{{ formset.management_form }}
<div class="link-formset">
{% for choice in formset %}
<div>
<label>{{ choice.media }}</label>
<input type="radio" name="featured">{{choice.featured_image.label }}
</div>
{% endfor %}
</div>
<input type="submit" value="{{ submit_btn }}">
</form>
forms.py
class ProductImagesForm(forms.ModelForm):
media = forms.ImageField(label='Image', required=True)
featured_image = forms.BooleanField(initial=True, required=True)
class Meta:
model = ProductImages
fields = ['media', 'featured_image']
ImagesFormset = modelformset_factory(ProductImages, fields=('media', 'featured_image'), extra=5)
views.py
def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def form_valid(self, form, formset):
return HttpResponseRedirect(self.get_success_url())
def get(self, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(queryset=ProductImages.objects.none())
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(self.request.POST, self.request.FILES or None)
form_valid = form.is_valid()
formset_valid = formset.is_valid()
if (form.is_valid() and formset.is_valid()):
seller = self.get_account()
form.instance.seller = seller
self.object = form.save()
images_set = formset.save(commit=False)
for img in images_set:
img.product = self.object
img.save()
formset.save()
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)
You can always pass in min_num and max_num to the modelformset_factory
ImagesFormset = modelformset_factory(ProductImages,
fields=('media', 'featured_image'),
min_num=1,
max_num=5,
extra=5)
This will ensure that there is at least 1 image and max 5
Related
Is there any what to change data display in readonly InputField of ModelChoiceField, but retain the primary key of the object for submitting the form?
views.py
class BookingCreateView(LoginRequiredMixin, CreateView):
login_url = 'login'
form_class = BookingForm
template_name = 'booking_add.html'
success_url = reverse_lazy('booking_list')
def get_initial(self):
initial = super(BookingCreateView, self).get_initial()
initial['date'] = datetime.datetime.strptime(self.request.GET.get('date'), '%d-%m-%Y')
initial['room'] = get_object_or_404(Room, id=self.request.GET.get('room'))
initial['start'] = get_object_or_404(Period, number=self.request.GET.get('start'))
return initial
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
forms.py
class BookingForm(forms.ModelForm):
class Meta:
model = Booking
fields= ['room', 'date', 'start', 'end']
def __init__(self, *args, **kwargs):
initial_args = kwargs.get('initial', None)
if initial_args:
super(BookingForm, self).__init__(*args, **kwargs)
self.fields['room'].widget = forms.TextInput()
self.fields['start'].widget = forms.TextInput()
self.fields['room'].widget.attrs['readonly'] = True
self.fields['date'].widget.attrs['readonly'] = True
self.fields['start'].widget.attrs['readonly'] = True
self.fields['end'].queryset = Period.objects.get_available_periods(
initial_args['room'], initial_args['date'], initial_args['start'])
def clean(self):
cleaned_data = super(BookingForm, self).clean()
now = timezone.localtime(timezone.now())
bookings = Booking.objects.filter(room=cleaned_data['room'], date=cleaned_data['date'])
booking_start_time = datetime.datetime.combine(cleaned_data['date'], cleaned_data['start'].start, timezone.get_current_timezone())
booking_end_time = datetime.datetime.combine(cleaned_data['date'], cleaned_data['end'].end, timezone.get_current_timezone())
for booking in bookings:
if booking.check_overlap(booking_start_time, booking_end_time):
raise forms.ValidationError
if now > datetime.datetime.combine(cleaned_data['date'],
cleaned_data['start'].end, timezone.get_current_timezone()):
raise forms.ValidationError
return cleaned_data
booking_add.html
{% block content %}
<main>
<div class="reg-form">
<form class="form" method="post" action="">
{% csrf_token %}
<label for="room">Phòng</label>
{{ form.room }}
<label for="date">Ngày</label>
{{ form.date }}
<label for="start">Ca bắt đầu</label>
{{ form.start }}
<label for="end">Ca kết thúc</label>
{{ form.end }}
<button type="submit">Đăng ký</button>
</form>
</div>
</main>
{% endblock %}
The page is rendered like this:
The thing I want is that the input below label 'Phòng' which mean Room, filled with the room object method str() not the primary key of Room object and the submitting process still send the primary key.Is there any way to achieve that? Note: the first three fields need to be read only and their data are given via GET request.
I know that however I want the field room to be read-only and select widget doesn't have that attribute.
views.py
#login_required(login_url='/account/login/')
def TaskCreateView(request,pk,todo_id):
if not request.user.is_authenticated:
return redirect('accounts:index')
else:
instance = get_object_or_404(Level, pk=pk)
qs = instance.todo_set.get(id = todo_id)
todo = Task.objects.filter(todo=qs, student=request.user)
if todo.exists():
messages.warning(request, 'You Already Completed This Task')
return HttpResponseRedirect(instance.get_absolute_url())
form = StudentTaskForm(request.POST or None, request.FILES or None)
if form.is_valid():
form.instance.user = User.objects.get(id=request.user.id)
obj = form.save(commit=False)
obj.student = request.user
obj.todo = qs
obj.level = instance
obj.save()
ImageFormSet = modelformset_factory(Images,
form=ImageForm, extra=3)
formset = ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
if request.method == 'POST':
if formset.is_valid():
for form in formset.cleaned_data:
image = form['image']
photo = Images(post=form, image=image)
photo.save()
return redirect('student:dashboard')
return render(request,'task_form.html',
{'form':form,"qs":qs,'formset':formset})
forms.py
class StudentTaskForm(forms.ModelForm):
title = forms.CharField(widget=forms.TextInput(attrs={'class':
'form-control',' type': "text",'placeholder':'Enter Title'}))
content = forms.CharField(widget=SummernoteWidget())
class Meta:
model = Task
fields = [
'title',
'content',
]
widgets = {
'content': SummernoteWidget(),
}
class ImageForm(forms.ModelForm):
image = forms.ImageField(label='Image')
class Meta:
model = Images
fields = ('image', )
I have two models Task and Images and I'm using two forms for the same.
Im trying to implement multiple image upload for the same. When I try to load the form I'm encountering this error. I have added the {{ formset.management_form }} in the template. The images model has a foreign key to the Task.
template:
<form id="post_form" action="" method="post"
enctype="multipart/form-data">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form %}
{{ field }} <br />
{% endfor %}
{{ formset.management_form }}
{% for form in formset %}
{{ form }}
{% endfor %}
<div class="panel-body">
<button type="submit" class="btn btn-primary m-t-10">Submit</button>
</div>
</form>
You should only instantiate the formset with request.POST for POST requests.
if request.method == 'POST':
formset = ImageFormSet(request.POST, request.FILES,
queryset=Images.objects.none())
...
else:
# GET request
formset = ImageFormSet(queryset=Images.objects.none())
...
I have a model form to update the user profile and everything is saving correctly except of them image. If I use admin it updates fine but when I use my form it just stays as the default profile image.
Here is my form:
class EditProfileForm(forms.ModelForm):
birth_date = forms.DateField(label='birth_date', input_formats=['%Y-%m-%d'])
class Meta:
model = UserProfile
fields = (
"image",
"bio",
"location",
"birth_date",
)
Here is my model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
Here is my view:
def edit_profile(request):
instance = get_object_or_404(UserProfile, user=request.user)
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=instance)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
return redirect('/')
else:
form = EditProfileForm(instance=request.user)
return render(request, 'edit_profile.html', {'form': form})
And here is my html:
{% extends 'base.html' %}
{% block content %}
<h1>Edit Profile</h1>
<form method='POST' action=''>{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-outline-success">Save</button>
</form>
</body>
{% endblock %}
For file upload you need to specify form's enctype:
<form method='POST' action='' enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-outline-success">Save</button>
</form>
And you should pass request's files to form instance in view:
form = EditProfileForm(request.POST, request.FILES, instance=instance)
Check this doc for details.
I need to get value from tag and redirect to '/value/' by submit.
Now I'm getting:
'ApartmentForm' object has no attribute 'cleaned_data'
I'm totally missing something.
forms.py
class ApartmentForm(ModelForm):
class Meta:
model = Apartment
fields = ['title']
views.py
def index(request):
context = {}
context['apartments'] = get_list_or_404(Apartment)
if request.method == 'POST':
form = ApartmentForm(request.POST or None)
context = {'form': form,}
id = form.cleaned_data.get('id', None)
return redirect(id)
else:
context['apartment_form'] = ApartmentForm
return render(request, 'main/main.html', context)
template
<form action="/{{ apartment.id }}" method="post">
{% csrf_token %}
<p><select size="10">
<option disabled>Chose apartment</option>
{% for apartment in apartments %}
<option value="{{ apartment.id }}">{{ apartment.title }}</option>
{% endfor %}
</select></p>
<input type="submit" value="Chose">
</form>
Update
Thanks Daniel!
It was such a fail not to call form in the template. I fixed that, but it still passing None to url (http://127.0.0.1:8000/None). Can't figure out why.
view.py
def index(request):
context = {}
context['apartments'] = get_list_or_404(Apartment)
if request.method == 'POST':
form = ApartmentForm(request.POST or None)
if form.is_valid():
id = form.cleaned_data.get('id', None)
return redirect(id)
else:
context['apartment_form'] = ApartmentForm
return render(request, 'main/main.html', context)
forms.py
APARTMENTS = ()
for apartment in Apartment.objects.all():
APARTMENTS += ((apartment.id, apartment.title),)
class ApartmentForm(Form):
apartment = ChoiceField(label='', widget=Select, choices=APARTMENTS)
template
<form action="." method="post">
{% csrf_token %}
{{ apartment_form }}
<input type="submit" value="Chose">
form.cleaned_data comes from form.is_valid(), so you sould change your views.py like this:
def index(request):
[...]
if request.method == 'POST':
form = ApartmentForm(request.POST or None)
if form.is_valid():
[...]
id = form.cleaned_data.get('id', None)
return redirect(id)
I'm not sure how to filter dropdown based on user id.
Not I want for user id 2.
I want exactly like this for user id 2.
Model
#python_2_unicode_compatible # only if you need to support Python 2
class PredefinedMessage(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length=50)
list_description = models.CharField(max_length=50)
def __str__(self):
return self.list_name
class PredefinedMessageDetail(models.Model):
predefined_message_detail = models.ForeignKey(PredefinedMessage)
message = models.CharField(max_length=5000)
View
class PredefinedMessageDetailForm(ModelForm):
class Meta:
model = PredefinedMessageDetail
fields = ['predefined_message_detail', 'message']
exclude = ('user',)
def predefined_message_detail_update(request, pk, template_name='predefined-message/predefined_message_detail_form.html'):
if not request.user.is_authenticated():
return redirect('home')
predefined_message_detail = get_object_or_404(PredefinedMessageDetail, pk=pk)
form = PredefinedMessageDetailForm(request.POST or None, instance=predefined_message_detail)
if form.is_valid():
form.save()
return redirect('predefined_message_list')
return render(request, template_name, {'form':form})
html file
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
You can do it in view itself using
form = PredefinedMessageDetailForm(request.POST or None, instance=predefined_message_detail)
form.fields["predefined_message_detail"].queryset= PredefinedMessage.objects.filter(user=request.user)
But filtering happens based on request.user so it should be logged in.Consider that also. Hope this helps