CKEditor doesn't save data - django

I'm using CKEditor for a form. In the admin it works fine, but when using it in the ModelForm of a CreateView the editor doesn't save data. As in the official docs, with this code:
class EventForm(forms.ModelForm):
description = forms.CharField(widget=CKEditorWidget())
image = forms.ImageField(widget=forms.ClearableFileInput(), required=False)
class Meta:
model = Event
fields = ['title', 'description', 'type', 'start_date', 'end_date', 'fee']
And this html:
<div>
<form hx-post="{{ request.path }}" enctype="multipart/form-data" class="modal-content">
{% csrf_token %}
<div class="modal-header">
<h1>Create new event</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{form.media}}
{{form.as_p}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<input type="submit" value="Submit">
</div>
</form>
</div>
It won't let me submit the form as it will keep saying that the description field is required. Trying to add the CKEditor widget field in the init method, with this code:
class EventForm(forms.ModelForm):
image = forms.ImageField(widget=forms.ClearableFileInput(), required=False)
class Meta:
model = Event
fields = ['title', 'description', 'type', 'start_date', 'end_date', 'fee']
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
self.fields['start_date'].widget = forms.SelectDateWidget()
self.fields['end_date'].widget = forms.SelectDateWidget()
self.fields['description'].widget = CKEditorWidget()
The form will be sent, and the instance created. However, the 'description' field will be empty even if I enter some content. This is my view:
class CreateEvent(LoginRequiredMixin, CreateView):
model = Event
form_class = EventForm
template_name = 'events/events_form.html'
success_url = reverse_lazy('events:index')
def form_valid(self, form):
form.instance.author = self.request.user
event_obj = form.save(commit=True)
image = self.request.FILES.get('image')
if image:
EventImage.objects.create(title=event_obj.title, image=image, event=event_obj)
return HttpResponse(status=204, headers={'HX-Trigger' : 'eventsListChanged'})
How should I make sure the data is being saved from the CKeditor?

In models.py
from ckeditor.fields import RichTextField
In your model
description = RichTextField()
You don't need to do anything in the forms.py except putting it in the fields list

You can use hx-vals to pass description value manually.
Something like this should work:
hx-vals="js:{description: CKEDITOR.instances.id_description.getData()}"

Related

Django ManyToMany .add() doesn't add the elements to the created object

This is in my template:
<form hx-post="{% url 'orders:create' %}">
{% csrf_token %}
{% for service in venue.services.all %}
<input type="checkbox" name="services" value="{{ service.id }}"> {{ service.name }}<br>
{% endfor %}
<button
hx-include="[name='id']"
type="submit"
class="btn btn-primary btn-lg ">
<input type="hidden" value="{{ venue.id }}" name="id">
Submit
</button>
</form>
And this is the view:
class OrderCreateView(CreateView):
model = Order
form_class = OrderForm
template_name = "orders/order_page.html"
success_url = reverse_lazy("orders:success")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["venues"] = Venue.objects.all()
return context
def form_valid(self, form):
if self.request.htmx:
# Get the IDs of the chosen services
service_ids = self.request.POST.getlist('services')
# Set the venue field of the form
form.instance.venue = Venue.objects.get(id=self.request.POST.get("id"))
# Save the form
self.object = form.save()
# Add the chosen services to the Order object
for service_id in service_ids:
service = Service.objects.get(id=service_id)
self.object.chosen_services.add(service)
return super().form_valid(form)
The problem is that the object is being created but only the line with form.instance.venue works, the part where the chosen_services are being added doesn't work, the object is created without any of them.
The service_ids variable is populated with the information from the front end, it has the ids that i need, it just doesn't add them to the object.
This is models.py:
class Order(models.Model):
venue = models.ForeignKey(Venue, on_delete=models.SET_NULL, null=True)
chosen_services = models.ManyToManyField(Service, null=True, blank=True)
Try this code
Here m2m field is already handled by form, you need to just set the value of the venue field with save() method of form
def form_valid(self, form):
if self.request.htmx:
# Get the IDs of the chosen services
service_ids = self.request.POST.getlist('services')
fm = form.save()
# Set the venue field of the form
fm.venue = Venue.objects.get(id=self.request.POST.get("id"))
fm.save()
return super().form_valid(form)
The problem was that in forms.py i had this:
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ["chosen_services"]
I deleted that and now it works!

How can I store a complaint made by a user in my admin panel by django

what do I write in my views.py and forms.py to store the complaints by a user:
This is how my site looks and this is where the users can input complaints. How can i save those complaints to later display, edit or delete them accordingly. How can I save those complaints in form of list in the admin panel.
models.py:
class Complaints(models.Model):
user = models.ForeignKey(User, on_delete= models.CASCADE, null = True, blank=True)
title = models.CharField(max_length=300)
description = models.TextField(null=True, blank= True)
highpriority = models.BooleanField(default=False)
document = models.FileField(upload_to='static/documents')
def __str__(self):
return self.title
What do I write in my views.py and forms.py to do this. Please help me. The basic function is to accept complaints so that associated people can receive it and resolve the comlpaints accordingly. How do I make the views and forms so that we can accept these complaints and store them somewhere accordingly?
template
<form class="" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-control col-lg-10 comp-title-field">{{form.title}}</div>
<p class="desc">Description</p>
<button type="button" class="btn btn-secondary preview-btn">Preview</button>
<div class="Descr ">{{form.description}}</div>
<button type="file" name="myfile" class="btn btn-secondary attach-btn"><i class="fas fa-file-upload"></i> Attachment</button>
<button type="submit" class="btn btn-secondary save-btn" value="submit"><i class="fas fa-save"></i> Save</button>
</form>
please check below and do minor changes according to your logic
#forms.py
from django.forms import ModelForm
import your model
class ComplaintForm(ModelForm):
class Meta:
model = Complaints
fields = ['title', 'description', 'highpriority', 'document']
views.py
import your form
def view_name(request):
user = request.user
if request.method == 'POST':
form = ComplaintForm(request.POST)
if form.is_valid():
form.save(commit=False)
form.user = request.user
form.save()
return render(request,your_template_path,ypur_context)
return render(request,your_template_path,ypur_context)
else:
form = ComplaintForm()
return render(request,your_template_path,ypur_context)

How can I use my modelForm field inside of a bootstrap form field?

my forms.py look like this
class UserRegistration(UserCreationForm):
def __int__(self, *args, **kwargs):
super(UserRegistration, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-registrationForm'
self.helper.form_class = 'u-form--modern'
self.helper.form_method = 'post'
self.helper.form_action = 'submit_form'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = User
fields = ['first_name',
'username',
'last_name',
'email',
'password1',
'password2']
and my templates look like this( i display only 1 field cuz they are more or less similar)
<form class="js-validate mt-5" id="id-registrationForm" method="post">
<div class="js-form-message mb-4">
<label class="h6 small d-block text-uppercase "> Email address</label>
<div class="js-focus-state input-group u-form">
<input type="email" class="form-control u-form__input" name="email"
placeholder="your#email.com">
</div>
</div>
Unfortunately my front-end skills are close to 0 and I could not really find out how to use my {{ form.email }} inside of a bootstrap's premade form. I need this to submit my modelForm and register a user.
Just to make it clear, i want my django form look like the one on the bottom
It is simple! First install the bootstrap4 :
pip install django-bootstrap4
Then in your html file load bootstrap4 and your form :
{% load bootstrap4 %}
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-primary" value="submit">
</form>
I recommend you to change your form to be look like this:
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django import forms
class UserCreateForm(UserCreationForm):
class Meta():
fields = ['username', 'email', 'password1', 'password2']
model = get_user_model()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
I actually came up with the solution, with the help of django-widget-tweaks
{% load widget_tweaks %}
<div class="js-form-message mb-4">
<div class="js-focus-state input-group u-form u-form">
<label class="h6 small d-block text-uppercase "> Email address</label>
<div class="js-focus-state input-group u-form">
{{ form.email|add_class:"form-control u-form__input u-form" }}
</div>
</div>
At least for me, django widget tweaks was a lot more intuitive in how to add my custom css to the form
my forms.py
class UserRegistration(UserCreationForm):
email = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'email#email.com'}))
class Meta:
model = User
fields = ['first_name',
'username',
'last_name',
'email',
'password1',
'password2']

Why doesn't include show the fields of a form when reusing a template?

Good morning, I'm new to Django, I'm trying to include a template form, but it does not show the fields, just the save button.
Use Django 2.1. I summarize the code, so please can you help me. since you need to reuse the form in other templates.
models.py
from django.db import models
class Area(models.Model):
nombre = models.CharField(max_length=45)
descripcion = models.CharField(max_length=100, null=True, blank=True)
def __str__(self):
return self.nombre
views.py
class AreaCreate(CreateView):
model = Area
form_class = AreaForm
template_name = 'area/area_form.html'
class AreaList(ListView):
model = Area
template_name = 'area/area_list.html'
forms.py
class AreaForm(forms.ModelForm):
class Meta:
model = Area
fields = (
'nombre',
'descripcion',
)
widgets = {
'nombre': forms.TextInput(attrs={'class': 'form-control', 'placeholder':'Area'}),
'descripcion': forms.TextInput(attrs={'class': 'form-control', 'placeholder':'DescripciĆ³n'}),
}
area_form.html
<form action="" method="POST"> {% csrf_token %}
<div class="form-row">
<div class="col-sm-4 campoform">
{{ form.nombre }}
</div>
<div class="col-sm-6 campoform">
{{ form.descripcion }}
</div>
<div class="col-sm-2 campoform">
<button class="btn btn-primary" type="submit" style="width:100%">Guardar</button>
</div>
</div>
</form>
area_list.html --- I am not adding the complete list.html code for a better understanding.
<div>{% include "area/area_form.html" %}</div>
The result in area_list.html is that it shows only the button.
Sorry for the delay in responding. I solved it by calling the form with a modal. Using the library "bootstrap_modal_forms".
#View
class GestionarAreas(generic.ListView):
model = Area
context_object_name = 'areas'
template_name = 'areas/gestionar_areas.html'
paginate_by = 10
class CrearArea(generic.CreateView):
template_name = 'areas/crear_area.html'
form_class = AreaForm
success_url = reverse_lazy('gestionar_areas')
#urls
path('', login_required(views.GestionarAreas.as_view()),name='gestionar_areas'),
path('crear/', login_required(views.CrearArea.as_view()), name='crear_area'),
#HTML
<div class="col-sm-auto">
<button class="crear-area btn btn-primary" type="button"name="button">
<span class="fa fa-plus mr-2"></span>Crear Area</button>
</div>
#Script
// Boton Crear Area
$(".crear-area").modalForm({formURL: "{% url 'crear_area' %}", successURL: ""});
// Boton Actualizar Area
$(".editar-area").each(function () {
$(this).modalForm({formURL: $(this).data('id'), successURL: ""});
});

CreateView and related model fields with fixed inital values

I'm working with a CreateView where I know what some of the field values will be ahead of time. In the example below, I know that the author field for a new Entry object will be the current user and I use get_initial() to preset this.
Now I would like to omit this field from my template form. I've tried several approaches:
Simply commenting out the author field in the form template leads to an invalid form.
Leaving 'author' out of fields. Nope.
And here's a related problem. The example below involves a relationship to a User object that exists. But what if I need to create an object, say an auth Group for editors? I've tried creating a placeholder group and renaming it ... and, well, that didn't work very well.
#
# model
#
class Author(Model):
name = CharField(max_length = 60)
user = OneToOneField(to = User, related_name = 'author_user', on_delete = CASCADE)
class Entry(Model):
title = CharField(max_length = 60)
author = ForeignKey(to = Author, related_name = 'entry_author')
#
# view
#
class EntryCreateView(CreateView):
model = Entry
fields = ('title', 'author')
def get_initial(self):
initial = super(EntryCreateView, self).get_initial()
initial['author'] = get_object_or_404(Author, user = self.request.user)
return initial
#
# template
#
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<label for="{{ form.title.id_for_label }}">Title:</label>
{{ form.title }}
<label for="{{ form.author.id_for_label }}">Author:</label>
{{ form.author }}
<p>
<input type="submit" class="btn btn-primary" name="save" value="Save" />
<input type="submit" class="btn btn-primary" name="cancel" value="Cancel" />
</form>
{% endblock %}
You can manually set user in form_valid() method of EntryCreateView class:
class EntryCreateView(CreateView):
model = Entry
fields = ('title',)
def form_valid(self, form):
user = self.request.user
form.instance.user = user
return super(EntryCreateView, self).form_valid(form)
You'll need to create a ModelForm for the customizations you need (https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/).
You can't remove author because it's required on your model currently.
Try something like this:
In forms.py...
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['title', 'author']
def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', {})
self.author = initial.get('author')
super(EntryForm, self).__init__(*args, **kwargs)
You can make modifications to the fields (set to not required, delete a field from the form fields, etc) in __init__ or on the class.
Just import and reference this form in your views to use it.