I've been working on a project lately and got the above error. It says " Error during template rendering".I have a similar model, which works perfectly fine. I've looked for similar errors but got none matching my situation. I don't know where I went wrong. It would be great if I get helpful answers.
Models.py
class ServiceTax(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE,related_name="service_tax",null=True,blank=True)
name=models.CharField(max_length=100)
percent=models.FloatField(default='0')
add_amount=models.IntegerField(default='0')
def __str__(self):
return self.name
Forms.py
class ServiceTaxForm(forms.ModelForm):
class Meta:
model = ServiceTax
fields = "__all__"
widgets = {
'name' : forms.TextInput(attrs={'class': 'form-control'}),
'percent' : forms.NumberInput(attrs={'class': 'form-control','step':'0.01'}),
'add_amount' : forms.NumberInput(attrs={'class':'form-control','maxlength':5}),
}
labels={
'add_amount': "Additional Amount"
}
Views.py
def tax_form(request,id=0):
if request.method == 'GET':
if id == 0:
form = ServiceTaxForm(request)
else:
tax = ServiceTax.objects.get(pk=id)
if tax in request.user.service_tax.all():
form = ServiceTaxForm(request,instance=tax)
else:
return redirect('/revenue/tax')
return render(request,'tax-form.html',{'form':form})
else:
if id==0:
form = ServiceTaxForm(request,request.POST)
if form.is_valid():
name = form.cleaned_data["name"]
percent = form.cleaned_data["percent"]
add_amount = form.cleaned_data["add_amount"]
t = AnnualTax(
name=name,
percent=percent,
add_amount=add_amount,
)
t.save()
request.user.service_tax.add(t)
else:
tax = ServiceTax.objects.get(pk=id)
if tax in request.user.service_tax.all():
form = ServiceTaxForm(request,request.POST,instance=tax)
if form.is_valid():
name = form.cleaned_data["name"]
percent = form.cleaned_data["percent"]
add_amount = form.cleaned_data["add_amount"]
tax_obj = ServiceTax.objects.get(pk=id)
tax_obj.name = name
tax_obj.percent = percent
tax_obj.add_amount = add_amount
tax_obj.save()
return redirect('/revenue/tax')
tax-form.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<i class="fa fa-chevron-circle-left fa-3x m-2"></i>
<div class="content">
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h4 class="card-title">Add Service Tax</h4>
</div>
<div class="card-body">
<form action="" method="POST" autocomplete="off">
{% csrf_token %}
<div class="row">
<div class="col-md-4 pr-1">
<div class="form-group">
{{ form.name | as_crispy_field}}
</div>
</div>
<div class="col-md-4 pr-1">
<div class="form-group">
{{ form.percent | as_crispy_field}}
</div>
</div>
<div class="col-md-4 pr-1">
<div class="form-group">
{{ form.add_amount | as_crispy_field}}
</div>
</div>
</div>
<button type="submit" class="btn btn-success btn-fill pull-right">
{% if request.get_full_path == '/income/tax/add/' %}
Add Tax
{% else %}
Update
{% endif %}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
You should retrieve your forms passing request.METHOD, not just request
As an example this piece of code form = ServiceTaxForm(request) should be form = ServiceTaxForm(request.POST)
Related
I have this view with two forms.
def anunciocreateview(request):
anuncio_form = AnuncioForm(request.POST or None)
producto_form = ProductoForm(request.POST or None)
if request.method == "POST":
if all([anuncio_form.is_valid(), producto_form.is_valid(), imagen_form.is_valid()]):
anuncio = anuncio_form.save(commit=False)
anuncio.anunciante = request.user
anuncio.save()
producto = producto_form.save(commit=False)
producto.anuncio = anuncio
producto.save()
return HttpResponse(status=204, headers={'HX-Trigger' : 'eventsListChanged'})
else:
anuncio_form = AnuncioForm()
producto_form = ProductoForm()
context = {
'anuncio_form' : anuncio_form,
'producto_form' : producto_form,
}
return render(request, 'buyandsell/formulario.html', context)
This view works OKAY; it allows the user to create instances of both models with the correct relation. I'm trying to add another form for the image of the product. I tried adding this:
def anunciocreateview(request):
anuncio_form = AnuncioForm(request.POST or None)
producto_form = ProductoForm(request.POST or None)
imagen_form = ImagenForm(request.POST, request.FILES)
if request.method == "POST":
if all([anuncio_form.is_valid(), producto_form.is_valid(), imagen_form.is_valid()]):
anuncio = anuncio_form.save(commit=False)
anuncio.anunciante = request.user
anuncio.save()
producto = producto_form.save(commit=False)
producto.anuncio = anuncio
producto.save()
imagen = imagen_form.request.FILES.get('imagen')
if imagen:
Imagen.objects.create(producto=producto, imagen=imagen)
return HttpResponse(status=204, headers={'HX-Trigger' : 'eventsListChanged'})
else:
print(request.FILES)
else:
anuncio_form = AnuncioForm()
producto_form = ProductoForm()
imagen_form = ImagenForm()
context = {
'anuncio_form' : anuncio_form,
'producto_form' : producto_form,
'imagen_form' : imagen_form
}
return render(request, 'buyandsell/formulario.html', context)
But this happens:
1- The 'Image upload' form field shows error 'This field is required' at the moment of rendering the form.
2- If uploading an image and clicking submit, the redirect doesn't happen as the imagen_form is not marked as valid. No error pops in the console whatsoever (I'm handling requests with HTMX).
If I omit the 'request.FILES' when instantiating the form, the error when the form renders disappear, but it anyway doesn't upload the image nor the form.
What am I missing?
For reference, here is the HTML file:
<form class="row g-3" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Create new listing</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="{{ anuncio_form.titulo.auto_id }}">{{ anuncio_form.titulo.label }}</label>
{% render_field anuncio_form.titulo|add_error_class:"is-invalid" class="form-control" %}
</div>
<div class="mb-3">
<label for="{{ anuncio_form.envio.auto_id }}">{{ anuncio_form.envio.label }}</label>
{% translate "Select available delivery options" as input_place_holder %}
{% render_field anuncio_form.envio|add_error_class:"is-invalid" class="form-control" placeholder=input_place_holder %}
</div>
<h5>Product:</h5>
<div class="mb-3">
<label for="{{ producto_form.nombre.auto_id }}">{{ producto_form.nombre.label }}</label>
{% translate "Name of your product" as input_place_holder %}
{% render_field producto_form.nombre|add_error_class:"is-invalid" class=" form-control" placeholder=input_place_holder %}
</div>
<div class="mb-3">
<label for="{{ producto_form.descripcion.auto_id }}">{{ producto_form.descripcion.label }}</label>
{% translate "Give a detailed description of your product" as input_place_holder %}
{% render_field producto_form.descripcion|add_error_class:"is-invalid" class=" form-control" placeholder=input_place_holder %}
</div>
<div class="row g-3">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ producto_form.estado.auto_id }}">{{ producto_form.estado.label }}</label>
{% render_field producto_form.estado|add_error_class:"is-invalid" class=" form-control" %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ producto_form.cantidad.auto_id }}">{{ producto_form.cantidad.label }}</label>
{% render_field producto_form.cantidad|add_error_class:"is-invalid" class=" form-control" %}
</div>
</div>
</div>
<div class="row g-3">
<div class="col-md-6">
<div class="mb-3">
<label for="{{ producto_form.precio_unitario.auto_id }}">{{ producto_form.precio_unitario.label }}</label>
{% render_field producto_form.precio_unitario|add_error_class:"is-invalid" class=" form-control" %}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="{{ producto_form.disponibilidad.auto_id }}">{{ producto_form.disponibilidad.label }}</label>
{% render_field producto_form.disponibilidad|add_error_class:"is-invalid" class=" form-control" %}
</div>
</div>
</div>
<h5>Images:</h5>
<div class="mb-3">
<label for="{{ imagen_form.imagen.auto_id }}">{{ imagen_form.imagen.label }}</label>
{% render_field imagen_form.imagen|add_error_class:"is-invalid" class=" form-control" %}
</div>
<div id="productforms"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-tertiary" hx-get="{% url 'buyandsell:create-product' %}" hx-target="#productforms" hx-swap="beforeend">Add new product</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary" hx-post="{% url 'buyandsell:createview' %}">Submit</button>
</div>
</div>
</form>
Edit:
forms.py for ImagenForm:
class ImagenForm(ModelForm):
class Meta:
model = Imagen
fields = ['imagen']
models.py for the Imagen model:
class Imagen(models.Model):
producto = models.ForeignKey(Producto, on_delete=models.CASCADE, related_name='imagen', blank=True, null=True)
imagen = models.ImageField(upload_to='marketplace/')
def __str__(self):
return f"Imagen de {self.producto}"
def save(self, *args, **kwargs):
self.image = resize_image(self.image, size=(350, 350))
super().save(*args, **kwargs)
Don't initialize forms with request.POST if it is not a POST request.
Two changes to fix your issue:
move first form init block under if request.method == "POST"
move else block one tab to the left so it becomes else to if request.method == "POST" instead of current version where it is else to if .is_valid()
if request.method == "POST":
anuncio_form = AnuncioForm(request.POST or None)
producto_form = ProductoForm(request.POST or None)
imagen_form = ImagenForm(request.POST, request.FILES)
if all([anuncio_form.is_valid(), producto_form.is_valid(), imagen_form.is_valid()]):
...
else:
anuncio_form = AnuncioForm()
producto_form = ProductoForm()
imagen_form = ImagenForm()
Now you use request.POST only on POST requests, instead you show empty forms. If a form is invalid on POST then it will be rendered with errors (if template is done cottectly) instead of showing empty forms back again.
For handling multiple forms with one view/template take a look at these answers: one two three
upd
For HTMX-requests it is also required to set hx-encoding="multipart/form-data" in form element attributes. Similar question HTMX docs
I am trying to make use of the crispy forms to display the form for inserting the data. I have a model as:
class Athlete(models.Model):
athlete_name=models.CharField(max_length=50)
GENDER_CHOICES=(
('M','Male'),
('F','Female'),
('O','Others')
)
gender=models.CharField(choices=GENDER_CHOICES,max_length=100)
age=models.IntegerField()
athlete_category=models.ForeignKey(Category,on_delete=models.CASCADE)
image=models.FileField(upload_to='static/athlete_img', null=True)
COUNTRY_CHOICES=(
('np','nepal'),
('in','india'),
('uk','united kingdom'),
('sp','spain'),
('ch','china')
)
medals=models.IntegerField
country=models.CharField(choices=COUNTRY_CHOICES,max_length=100)
def __str__(self):
return self.athlete_name
In the forms.py...I have modelform as:
class AthleteForm(ModelForm):
class Meta:
model:Athlete
fields='__all__'
In my views.py I have the following function:
def add_athlete(request):
if request.method == 'POST':
form = AthleteForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS,
'Athlete added sucessfully')
return redirect('/admin/athletes')
else:
messages.add_message(request, messages.ERROR,
'Enter the appropriate values')
return render(request, 'forgame/addathletes.html', {
'form': form
})
context = {
'form': AthleteForm
}
return render(request, 'forgame/addathletes.html', context)
Inside my templates/forgame I have created addathletes.html
{% extends 'layouts.html' %}
{% load crispy_forms_tags %}
{% block title %}
<title>Game Category</title>
{%endblock%}
{% block main_content %}
<div class="container-fluid mt-4">
<div class="d-flex justify-content-center">
<div class="col-md-6">
<h2>Add Categories Here!</h2>
{% for msg in messages %}
{% if msg.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %}
<div class="alert alert-success">
{{msg}}
</div>
{%endif%}
{% if msg.level == DEFAULT_MESSAGE_LEVELS.ERROR %}
<div class="alert alert-danger">
{{msg}}
</div>
{%endif%}
{%endfor%}
<form action="" method="post" class="shadow-lg p-3">
{%csrf_token%}
{{form | crispy}}
<div class="mt-3">
<input type="submit" value="Add Category" class="btn btn-primary">
</div>
</form>
</div>
</div>
</div>
{% endblock %}
My urls looks fine but I have been getting this error:
Along with this:
It should be = not : so:
class AthleteForm(ModelForm):
class Meta:
model = Athlete
fields='__all__'
I'd also recommend you to maintain gaps between template tags like it should be {% endblock %} not {%endblock%} same goes for every tag.
I have a chat code in which I want to display and suggest only those users with whom there has already been a correspondence, that is, both incoming and outgoing messages, and not all registered users.
html code:
<div class="container" style="height: 75%;">
<div class="card bg-dark h-100 border-light">
<div class="card-body h-100">
<div class="row h-100">
<div class="col-md-4 border-right h-100">
<div class="list-group bg-dark" id='user-list'>
{% for u in users %} {% if not u.id == 1 and not u.id == user.id %}
<a class="list-group-item {% if u.id != chat_id %}bg-dark{% else %}bg-primary{% endif %} text-white" href="{% url 'chat-home' %}?u={{u.id}}">
<div>
<p>{{u.first_name}} {{u.last_name}} ({{u.username}})</p>
</div>
</a>
{% endif %} {% endfor %}
</div>
</div>
<div class="col-md-8 h-100">
{% if not chat_id > 0 %}
<div class="h-100 d-flex flex-column justify-content-center align-items-center">
<h3>Начните общение!</h3>
<p><small class="text-muted">Выберете человека, чтобы написать ему.</small></p>
</div>
{% else%}
<div id="chat-box-field" class="h-100">
<div class="chat-box" style="height:80%">
{% for chat in chats %} {% if chat.user_from == user %}
<div class="p-2 w-100 d-flex justify-content-end">
<div class=" chat-bubble ml-2 mb-2 bg-primary text-light rounded" data-id="{{chat.id}}">
<p>{{chat.message}}</p>
<div class="d-flex justify-content-between"><small>Ты</small> <small>{{chat.date_created|date:"M-d-Y H:i"}}</small></div>
</div>
</div>
{% else %}
<div class="p-2 w-100 d-flex justify-content-start">
<div class="chat-bubble mr-2 mb-2 bg-light text-dark rounded" data-id="{{chat.id}}">
<p>{{chat.message}}</p>
<div class=" d-flex justify-content-between"><small>От</small> <small>{{chat.date_created|date:"M-d-Y H:i"}}</small></div>
</div>
</div>
{% endif %} {% endfor %}
</div>
<div class="chat-box-form border-top p-2" style="height:20%">
<div class="w-100 h-100">
<form action="" id="chat-submit" class="h-100 d-flex ">
<input type="hidden" name="user_from" value="{{ user.id }}">
<input type="hidden" name="user_to" value="{{ chat_id }}">
<div class="col-md-10 h-100">
<textarea name="message" id="" class="h-100 w-100 form-control" placeholder="Написать"></textarea>
</div>
<button class="button btn btn-primary h-100 w-100 justify-content-center align-items-center d-flex">Отправить</button>
</form>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
views.py:
def get_messages(request):
chats = chatMessages.objects.filter(Q(id__gt=request.POST['last_id']),Q(user_from=request.user.id, user_to=request.POST['chat_id']) | Q(user_from=request.POST['chat_id'], user_to=request.user.id))
new_msgs = []
for chat in list(chats):
data = {}
data['id'] = chat.id
data['user_from'] = chat.user_from.id
data['user_to'] = chat.user_to.id
data['message'] = chat.message
data['date_created'] = chat.date_created.strftime("%b-%d-%Y %H:%M")
print(data)
new_msgs.append(data)
return HttpResponse(json.dumps(new_msgs), content_type="application/json")
def send_chat(request):
resp = {}
User = get_user_model()
if request.method == 'POST':
post =request.POST
u_from = UserModel.objects.get(id=post['user_from'])
u_to = UserModel.objects.get(id=post['user_to'])
insert = chatMessages(user_from=u_from,user_to=u_to,message=post['message'])
try:
insert.save()
resp['status'] = 'success'
except Exception as ex:
resp['status'] = 'failed'
resp['mesg'] = ex
else:
resp['status'] = 'failed'
return HttpResponse(json.dumps(resp), content_type="application/json")
models.py:
class chatMessages(models.Model):
user_from = models.ForeignKey(User,
on_delete=models.CASCADE,related_name="+")
user_to = models.ForeignKey(User,
on_delete=models.CASCADE,related_name="+")
message = models.TextField()
date_created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.message
Please tell me how it can be implemented. I don't quite understand how to set a condition for checking for messagesm
You probably need to change the related name first on the model:
class chatMessages(models.Model):
user_from = models.ForeignKey(User,
on_delete=models.CASCADE,related_name="sent")
user_to = models.ForeignKey(User,
on_delete=models.CASCADE,related_name="received")
message = models.TextField()
date_created = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.message
And then to get all messages the user has received:
messages = request.user.received.all()
(Or User.objects.get(id=my_user_id).received.all())
To get all the users which the user has corresponded with, you can then do:
pk_list = messages.values("user_from__pk").distinct()
correspondents = get_user_model().objects.filter(pk__in=list(pk_list))
Sidenote:
In your views, you are getting the user model.
User = get_user_model()
But you don't do anything with it in this view:
def send_chat(request):
In a view I have 3 forms:
forms.py
class PostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(PostForm, self).__init__(*args, **kwargs)
class Meta:
model = Post
fields = ['subtitle', 'latitude', 'longitude', 'tags', 'body']
widgets = {
'body': forms.Textarea(attrs={'class': 'log-body editable'}),
'tags': forms.TextInput(attrs={'id': 'tags-field', 'class': 'log-form-field'}),
'latitude': forms.TextInput(attrs={'id': 'latitude-field', 'class': 'log-form-field'}),
'longitude': forms.TextInput(attrs={'id': 'longitude-field', 'class': 'log-form-field'}),
'subtitle': forms.TextInput(attrs={'id': 'subtitle-field', 'class': 'log-form-field'}),
}
labels = {
'subtitle': 'Subtitle:',
'tags': 'Tags:',
'latitude': 'Latitude',
'longitude': 'Longitude',
'body': ''
}
class ImageForm(forms.ModelForm):
images = forms.ImageField(label='Images', widget=forms.ClearableFileInput(
attrs={
'multiple': True,
'class': 'file-upload',
'id': 'file-upload-button-image',
'name': 'images'}
)
)
class Meta:
model = PostImage
fields = ('images',)
class VideoForm(forms.ModelForm):
videos = forms.FileField(label='Videos', widget=forms.ClearableFileInput(
attrs={
'multiple': True,
'class': 'file-upload',
'id': 'file-upload-button-video',
'name': 'images'
}
)
)
class Meta:
model = PostVideo
fields = ('videos',)
And I have a template that creates a post and stores the videos and pictures and that works great. Where I'm having a hard time is updating a 'Post'.
class PostEditView(TemplateView):
template_name = 'Logging/edit view.html'
post_form_class = PostForm
image_form_class = ImageForm
video_form_class = VideoForm
def get_context_data(self, **kwargs):
context = super(PostEditView, self).get_context_data(**kwargs)
context['post_pk'] = self.kwargs['pk']
context['post_form'] = PostForm(instance=Post.objects.get(pk=self.kwargs['pk']))
context['image_form'] = ImageForm(empty_permitted=True, use_required_attribute=False)
context['video_form'] = VideoForm(empty_permitted=True, use_required_attribute=False)
context['images'] = PostImage.objects.filter(post_id=self.kwargs['pk'])
context['videos'] = PostVideo.objects.filter(post_id=self.kwargs['pk'])
return context
def post(self, request, *args, **kwargs):
context = self.get_context_data()
post_data = request.POST or None
if post_data.get('Delete'):
if len(post_data.getlist('img-index[]')) > 0:
for i in post_data.getlist('img-index[]'):
PostImage.objects.filter(id=i).delete()
if len(post_data.getlist('vid-index[]')) > 0:
for i in post_data.getlist('vid-index[]'):
PostVideo.objects.filter(id=i).delete()
print(post_data)
post_form = PostForm(data=post_data, instance=Post.objects.get(pk=self.kwargs['pk']), prefix='post')
print(post_form.is_bound)
post_form.is_valid()
print(post_form.errors)
images = request.FILES.getlist('image-images')
videos = request.FILES.getlist('video-videos')
if post_form.is_valid():
print('valid form')
for i in images:
instance = PostImage(image=i, post=context['post_pk'])
instance.save()
for v in videos:
instance = PostVideo(video=v, post=context['post_pk'])
instance.save()
return super(PostEditView, self).render_to_response(context)
def form_save(self, form, request):
obj = form.save(commit=False)
obj.user = request.user
obj.author = request.user.first_name + ' ' + request.user.last_name
obj.event_type = 'Log Entry'
obj.save()
messages.success(self.request, "{} posted successfully".format(obj))
return obj
When I call is_valid() on post_form it always says false, although it does say its bound. My post object does contain the data from the form in my template, including a csrf token, but for some reason the form is never valid and says that all the fields are empty.
from print(post_data):
<QueryDict: {'csrfmiddlewaretoken': ['h5uxuACKsvBcO4lrPB7yKbYgr0BsASEklT3GTp6R3t8na13TadAfjbihl2VyGvdC'], 'subtitle': ['Test with photos'], 'latitude': ['48.000'], 'longitude': ['112.000'], 'tags': ['[]'], 'images': [''], 'videos': [''], 'body': ['<p>body test 2 saa</p>']}>
from print(post_form.is_bound):
True
from print(post_form.errors):
<ul class="errorlist">
<li>subtitle<ul class="errorlist"><li>This field is required.</li></ul></li><li>latitude
<ul class="errorlist"><li>This field is required.</li></ul></li><li>longitude
<ul class="errorlist"><li>This field is required.</li></ul></li><li>tags
<ul class="errorlist"><li>This field is required.</li
></ul></li><li>body
<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Models.py
class Post(models.Model):
class Status(models.TextChoices):
active = 'Active'
inactive = 'Inactive'
user = models.ForeignKey(Account, on_delete=models.CASCADE)
author = models.CharField(max_length=255)
title = models.CharField(max_length=80, default='Log Entry: ' + datetime.datetime.now().strftime("%m/%d/%Y, %H:%M:%S"))
subtitle = models.CharField(max_length=255)
latitude = models.DecimalField(decimal_places=3, max_digits=6)
longitude = models.DecimalField(decimal_places=3, max_digits=6)
post_time = models.DateTimeField(auto_now_add=True)
tags = TaggableManager()
body = models.TextField()
history = HistoricalRecords()
event_type = models.CharField(max_length=50, choices=EventType.choices, default='Log Entry', editable=False)
status = models.CharField(max_length=10, choices=Status.choices, default='Active')
def __str__(self):
return 'Post by: ' + str(self.user) + " on: " + self.post_time.strftime("%m/%d/%Y at %H:%M:%S")
def get_image_filename(instance, filename):
title = instance.post.title
slug = slugify(title)
return "post_media/%s/%s" % (slug, filename)
class PostImage(models.Model):
post = models.ForeignKey(Post, default=None, on_delete=models.PROTECT)
image = models.ImageField(upload_to=get_image_filename, verbose_name='Image')
def filename(self):
return self.image.name.split('/')[-1]
class PostVideo(models.Model):
post = models.ForeignKey(Post, default=None, on_delete=models.PROTECT)
video = models.FileField(upload_to=get_image_filename, verbose_name='Video')
def filename(self):
return self.video.name.split('/')[-1]
template:
{% extends 'layout/base.html' %}
{% load static %}
{% block titleblock %}New Log{% endblock %}
{% block header %}
<script src="{% static 'Logging/js/medium-editor.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'Logging/css/medium-editor.min.css' %}">
<link rel="stylesheet" href="{% static 'Logging/css/new_post.css' %}">
{% endblock %}
{% block bodyblock %}
<form class="post-form" id="post-form" enctype="multipart/form-data" method="POST" action=".">
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="Modal-title" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Confirm</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Are you sure you want to delete these items?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger" name="Delete" value="1" form="post-form">Delete</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-10 col-sm-12">
{% csrf_token %}
{% for field in post_form %}
{% ifnotequal field.label '' %}
<div class="input">
{{ field.label_tag }}
{{ field }}
</div>
<br>
{% endifnotequal %}
{% endfor %}
</div>
<div class="col-lg-2 col-sm-12 button-col">
<label for="file-upload-button-image" class="custom-file-upload">
<div class="upload-button-wrapper">
{{ image_form.images }}
<p id="image-num"></p>
<img src="{% static 'layout/img/image.svg' %}" alt="svg" id="img-svg">
<img src="{% static 'layout/img/plus-circle.svg' %}" alt="svg+" id="plus-svg">
</div></label>
<label for="file-upload-button-video" class="custom-file-upload">
<div class="upload-button-wrapper">
{{ video_form.videos }}
<p id="video-num"></p>
<img src="{% static 'layout/img/film.svg' %}" alt="svg" id="img-svg">
<img src="{% static 'layout/img/plus-circle.svg' %}" alt="svg+" id="plus-svg">
</div></label>
</div>
</div>
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-12 edit-col">
<div class="images_existing">
{% for image in images %}
<div class="stacker">
<div class="image-selector-wrapper">
<input type="checkbox" id="" name="img-index[]" class="invisible img-checkboxes" value="{{ image.pk }}">
<img class="thumbnail" src="{{ image.image.url }}" alt="{{ image.filename }}">
<h6>{{ image.filename }}</h6>
{% if forloop.first %}
<div class="num-indicator" id="image-number"></div>
{% endif %}
</div>
</div>
{% endfor %}
<div class="button-box">
<button type="submit" name="download" value="1" class="btn-primary">Download</button>
<button type="button" class="btn-danger" data-toggle="modal" data-target="#deleteModal">Delete</button>
</div>
</div><div class="edit" id="edit-images">Edit</div>
</div>
<div class="col-lg-4 col-md-6 col-sm-12 edit-col">
<div class="videos_existing">
{% for video in videos %}
<div class="stacker">
<div class="video-selector-wrapper">
<input type="checkbox" id="" name="vid-index[]" value="{{ video.pk }}" class="invisible video-cb">
<img class="thumbnail-video" src="{% static 'layout/img/film.svg' %}" alt="{{ video.filename }}">
<h6>{{ video.filename }}</h6>
{% if forloop.first %}
<div class="num-indicator" id="video-number"></div>
{% endif %}
</div></div>
{% endfor %}
</div><div class="edit" id="edit-video">Edit</div>
</div></div>
<div class="row">
<div class="col">
{{ post_form.body }}
<button type="submit" class="custom-submit-button"><img src="{% static 'layout/img/upload.svg' %}" alt="submit"> <span>Submit</span></button>
</div>
</div>
{% if post_form.errors %}
{% for field in post_form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in post_form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
</form>
<script>var editor = new MediumEditor('.editable');</script>
<script src="{% static 'Logging/js/new_post.js' %}"></script>
{% endblock %}
I can not for the life of my figure this out. Any help would be appreciated!
I am trying to edit multiple rows of data in a model via formsets. In my rendered form, I use javascript to delete (hide and assign DELETE) rows, and then save changes with post.
My view:
def procedure_template_modification_alt(request, cliniclabel, template_id):
msg = ''
clinicobj = Clinic.objects.get(label=cliniclabel)
template_id = int(template_id)
template= ProcedureTemplate.objects.get(templid = template_id)
formset = ProcedureModificationFormset(queryset=SectionHeading.objects.filter(template = template))
if request.method == 'POST':
print(request.POST.get)
# Create a formset instance with POST data.
formset = ProcedureModificationFormset(request.POST)
if formset.is_valid():
print("Form is valid")
instances = formset.save(commit=False)
print(f'instances:{instances}')
for instance in instances:
print(f'Instance: {instance}')
instance.template = template
instance.save()
msg = "Changes saved successfully."
print("Deleted forms:")
for form in formset.deleted_forms:
print(form.cleaned_data)
else:
print("Form is invalid")
print(formset.errors)
msg = "Your changes could not be saved as the data you entered is invalid!"
template= ProcedureTemplate.objects.get(templid = template_id)
headings = SectionHeading.objects.filter(template = template)
return render(request, 'procedures/create_procedure_formset_alt.html',
{
'template': template,
'formset': formset,
'headings': headings,
'msg': msg,
'rnd_num': randomnumber(),
})
My models:
class ProcedureTemplate(models.Model):
templid = models.AutoField(primary_key=True, unique=True)
title = models.CharField(max_length=200)
description = models.CharField(max_length=5000, default='', blank=True)
clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)
def __str__(self):
return f'{self.description}'
class SectionHeading(models.Model):
procid = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=200)
default = models.CharField(max_length=1000)
sortorder = models.IntegerField(default=1000)
fieldtype_choice = (
('heading1', 'Heading1'),
('heading2', 'Heading2'),
)
fieldtype = models.CharField(
choices=fieldtype_choice, max_length=100, default='heading1')
template = models.ForeignKey(ProcedureTemplate, on_delete=models.CASCADE, null=False)
def __str__(self):
return f'{self.name} [{self.procid}]'
My forms:
class ProcedureCrMetaForm(ModelForm):
class Meta:
model = SectionHeading
fields = [
'name',
'default',
'sortorder',
'fieldtype',
'procid'
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'class': 'form-control'})
self.fields['default'].widget.attrs.update({'class': 'form-control'})
self.fields['sortorder'].widget.attrs.update({'class': 'form-control'})
self.fields['fieldtype'].widget.attrs.update({'class': 'form-control'})
ProcedureCreationFormset = formset_factory(ProcedureCrMetaForm, extra=3)
ProcedureModificationFormset = modelformset_factory(SectionHeading, ProcedureCrMetaForm,
fields=('name', 'default', 'sortorder','fieldtype', 'procid'),
can_delete=True,
extra=0
# widgets={"name": Textarea()}
)
The template:
{% block content %} {% load widget_tweaks %}
<div class="container">
{% if user.is_authenticated %}
<div class="row my-1">
<div class="col-sm-2">Name</div>
<div class="col-sm-22">
<input type="text" name="procedurename" class="form-control" placeholder="Enter name of procedure (E.g. DNE)"
value="{{ template.title }}" />
</div>
</div>
<div class="row my-1">
<div class="col-sm-2">Description</div>
<div class="col-sm-22">
<input type="text" name="proceduredesc" class="form-control" placeholder="Enter description of procedure (E.g. Diagnostic Nasal Endoscopy)"
value="{{ template.description }}" />
</div>
</div>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %} {{ formset.management_form }}
<div class="row mt-3">
<div class="col-sm-1">Select</div>
<div class="col-sm-6">Heading</div>
<div class="col-sm-8">Default (Normal description)</div>
<div class="col-sm-2">Sort Order</div>
<div class="col-sm-4">Type</div>
<div class="col-sm-2">Action</div>
</div>
{% for form in formset %}
<div class="row procrow" id="row{{ forloop.counter0 }}">
<div class="" style="display: none;">{{ form.procid }}</div>
<div class="col-sm-6">
{{ form.name }}
</div>
<div class="col-sm-8">
<div class="input-group">
{{ form.default }}
</div>
</div>
<div class="col-sm-2">
<div class="input-group">
{{ form.sortorder }}
</div>
</div>
<div class="col-sm-4">
<div class="input-group">
{{ form.fieldtype }}
</div>
</div>
<div class="col-sm-2">
<div class="input-group">
<div class="input-group-append">
<button id="add{{ forloop.counter0 }}" class="btn btn-success add-row">+</button>
</div>
<div class="input-group-append">
<button id="del{{ forloop.counter0 }}" class="btn btn-danger del-row">-</button>
</div>
</div>
</div>
</div>
{% endfor %} {% endif %}
<div class="row my-3">
<div class="col-sm-8"></div>
<div class="col-sm-8">
<div class="input-group">
<div class="input-group-append mx-1">
<button id="save_report" type="submit" class="btn btn-success"><i class="fal fa-shield-check"></i>
Save Report Format</button>
</div>
<div class="input-group-append mx-1">
<button id="save_report" type="button" class="btn btn-danger"><i class="fal fa-times-hexagon"></i>
Cancel</button>
</div>
</div>
</div>
<div class="col-sm-8"></div>
</div>
<div>
{% for dict in formset.errors %} {% for error in dict.values %} {{ error }} {% endfor %} {% endfor %}
</div>
</form>
</div>
{% endblock %}
My data is displayed as below (Screenshot). I make changes with javascript when the delete button is pressed, so that the html becomes like this:
<div class="row procrow" id="row2" style="display: none;">
<div class="" style="display: none;"><input type="hidden" name="form-2-procid" value="25" id="id_form-2-procid"></div>
<div class="col-sm-6">
<input type="text" name="form-2-name" value="a" maxlength="200" class="form-control" id="id_form-2-name">
</div>
<div class="col-sm-8">
<div class="input-group">
<input type="text" name="form-2-default" value="v" maxlength="1000" class="form-control" id="id_form-2-default">
</div>
</div>
<div class="col-sm-2">
<div class="input-group">
<input type="number" name="form-2-sortorder" value="1000" class="form-control" id="id_form-2-sortorder">
</div>
</div>
<div class="col-sm-4">
<div class="input-group">
<select name="form-2-fieldtype" class="form-control" id="id_form-2-fieldtype">
<option value="heading1" selected="">Heading1</option>
<option value="heading2">Heading2</option>
</select>
</div>
</div>
<div class="col-sm-2">
<div class="input-group">
<div class="input-group-append">
<button id="add2" class="btn btn-success add-row">+</button>
</div>
<div class="input-group-append">
<button id="del2" class="btn btn-danger del-row">-</button>
</div>
</div>
</div>
<input type="checkbox" name="form-2-DELETE" id="id_form-2-DELETE" checked=""></div>
On submitting the data, I get the following output, and data does not reflect the deletion I did. It displays the same thing again. There are no errors. But my edited data is not being saved.
Output:
<bound method MultiValueDict.get of <QueryDict: {'csrfmiddlewaretoken': ['ka3avICLigV6TaMBK5a8zeVJlizhtsKW5OTDBLlYorKd7Iji9zRxCX2vvjBv6xKu'], 'form-TOTAL_FORMS': ['3'], 'form-INITIAL_FORMS': ['3'], 'form-MIN_NUM_FORMS': ['0'], 'form-MAX_NUM_FORMS': ['1000'], 'form-0-procid': ['23'], 'form-0-name': ['External ear canal'], 'form-0-default': ['Bilateral external ear canals appear normal. No discharge.'], 'form-0-sortorder': ['100'], 'form-0-fieldtype': ['heading1'], 'form-1-procid': ['24'], 'form-1-name': ['Tympanic membrane'], 'form-1-default': ['Tympanic membrane appears normal. Mobility not assessed.'], 'form-1-sortorder': ['500'], 'form-1-fieldtype': ['heading1'], 'form-2-procid': ['25'], 'form-2-name': ['a'], 'form-2-default': ['v'], 'form-2-sortorder': ['1000'], 'form-2-fieldtype': ['heading1'], 'form-2-DELETE': ['on']}>>
Form is valid
instances:[]
Deleted forms:
{'name': 'a', 'default': 'v', 'sortorder': 1000, 'fieldtype': 'heading1', 'procid': <SectionHeading: a [25]>, 'DELETE': True}
You actually need to delete the instances: Loop through the formsets' deleted_objects property after you called saved(commit=False) and delete them.