Django Mixin to add context variables - django

I want to create a GenericFormMixin for Django that I can mix into CreateView and UpdateView that will let me use a generic form template by dynamically setting things like the page title, form title, submit button text, etc.
I have the following in mixins.py:
class GenericFormMixin(object):
page_title = ''
form_title = ''
submit_button_text = ''
Instead of having to create a modelname_create.html and modelname_update.html for every CreateView or UpdateView I have, I want to be able to use generic_form.html which will use the mixin's variables in its context to dynamically create an appropriately populated template:
{% extends "base.html" %}
{% block title %}{{ page_title }}{% endblock title %}
{% block content %}
<div class="panel panel-primary">
<div class="panel-heading">{{ form_title }}</div>
<div class="panel-body">
<form method="post" action=".">
{{ form }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">{{ submit_button_text }}</button>
</form>
</div>
</div>
{% endblock content %}
My question is: now that I've created the mixin, how do I get the variables into the template context of CreateView and UpdateView?

Define get_context_data in your mixin. It should call the superclass method, add its elements to the dictionary returned from there, and then return the dict.

Related

ckeditor not saving changes django

I have a form where in one of the fields, I use the ckeditor. However when I submit the form, the changes in the ckeditor field is not being saved. In the model, I have changed the field to aRichTextField. I have installed "ckeditor" in my apps in settings as well.
I have also both tried to load these scripts in my template:
{% load static %}
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
On top of that have I also tried to add the {{ form.media }} instead of the scripts but it does still not work.
I am using HTMX to dynamically update the form.
This is my form template right now
<form action='' method="POST" class="form" hx-post='' hx-swap='outerHTML'>
{% csrf_token %}
{{ form.media }}
<div class="form-group">
{% for field in form %}
{{ field }}
</div>
{% endfor %}
<br>
<div class='htmx-indicator'>Loading...</div>
<div class="text-center">
<button class='htmx-inverted-indicator' type='submit' >Save</button>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
</form>
Does anybody know why the form is not being saved?
EDIT
This is my view
#login_required
def book_update_view(request, id=None):
book = get_object_or_404(Book, id=id)
form = BookForm(request.POST or None, instance=book)
context = {
"form": form,
"object": book,
}
if form.is_valid():
form.save()
context['message'] = 'Saved!'
if request.htmx:
return render(request, "book/snippets/forms.html", context)
return render(request, "book/update.html", context)
Looks like there is a conflict between the CKEditor and HTMX. The below relies heavily on this answer. It makes the following changes:
Switches the HTMX labels to the button rather than the form
Applies an event
listener to the CKEditor - it does this via the {{field.label_tag}}
which is now included
Fixes up a misplaced tag
Try making your form something like this (don't forget to replace the name of the CKEditor field - you may need to check your source code to see how this is rendered):
<form method="post">
{% csrf_token %}
{{ form.media }}
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
var element = new CKEDITOR.dom.element( document.getElementById( '{{ form.NAMEOFCKEDITORFIELD.id_for_label }}' ) );
event.detail.parameters['{{ form.NAMEOFCKEDITORFIELD.html_name }}'] = element.getEditor().getData();
})
</script>
<div class="form-group">
{% for field in form %}
{{ field.label_tag }}:<br />{{ field }}
{% endfor %}
</div>
<br>
<div class='htmx-indicator'>Loading...</div>
<div class="text-center">
<button class='htmx-inverted-indicator' type='submit' hx-post="{% url 'book_update_view_name' book.id %}" hx-target="#{{form.id}}" hx-swap="outerHTML">Save</button>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
if you dont want extra get parameter which might give you a problem, you can put onclick on your submit button to have ckeditor transfer the data to your field. something like this :
<script>
function saveCK(){
let ckdata = CKEDITOR.instances.your_field_id.getData();
$('#your_field_id').val(ckdata);
}
</script>
yeah, sorry for the jquery, I'm not sure vanilla javascript equivalent.
and on your submit button, add
onclick="saveCK()"
One more option is to use hx-vals.
For example:
<button type='submit' hx-vals="js:{ {{ form.NAMEOFCKEDITORFIELD.name }}: CKEDITOR.instances['{{ form.NAMEOFCKEDITORFIELD.id_for_label }}'].getData()}" hx-post=POSTURL>Save</button>

How to add classes to subforms within formset in django-crispy-forms

What I'm after is the ability to style forms within my formset:
<form>
{{ formset management stuff here }}
<div class="formset-child">
{{ formset child form here }}
</div>
<div class="formset-child">
{{ formset child form here }}
</div>
</form>
Is there a way to do this cleanly with the FormHelper (as for adding submit buttons etc.) or do I need to do it in my template and manually loop over the formset?
You can use layout.HTML() block to render the inline forms there like this:
layout_blocks = []
layout_blocks.append(layout.Fieldset(
_("Children"),
layout.HTML("""{% load crispy_forms_tags i18n %}
{{ formsets.children.management_form }}
<div id="children">
{% for form in formsets.children.forms %}
<div class="child formset-form">
{% crispy form %}
</div>
{% endfor %}
</div>
<!-- used by javascript -->
<div id="children_empty_form" class="child formset-form" style="display: none">
{% with formsets.children.empty_form as form %}
{% crispy form %}
{% endwith %}
</div>
"""),
css_id="children_fieldset",
))
layout_blocks.append(bootstrap.FormActions(
PrimarySubmit('submit', _('Save')),
))
self.helper.layout = layout.Layout(*layout_blocks)
Each of your inline forms can have the helper with its own layout.

Can't get POST data from Django form

I have a form involving just two textareas and I'm trying to POST them. However, I'm having difficulty trying to get the POST data from my view. Here's what I have so far in my template:
{% block title %}
<form method="post">
<strong>Title:</strong>
<div>
<textarea contenteditable id="id-title" name="title"></textarea>
</div>
{% endblock title %}
{% block description %}
<strong>Description:</strong>
<div>
<textarea contenteditable id="id-description" name="description"></textarea>
</div>
</form>
{% endblock description %}
<button class="btn-primary" id="ok" url="{% url 'publish' id=id %}" type="submit">Publish</button>
view:
#ajax
#csrf_exempt
#admin_required
def publish(request, id):
title = request.POST.get('title')
desc = request.POST.get('description')
print title
print desc
...
But title and desc turn out to be None. My publish function is also an ajax function that does some other stuff when the button is pressed.
Try altering your form with action, and moving your submit inside the form, e.g.:
<form method="post" action="{% url 'publish' id=id %}">
{% block title %}
<strong>Title:</strong>
<div>
<textarea contenteditable id="id-title" name="title"></textarea>
</div>
{% endblock title %}
{% block description %}
<strong>Description:</strong>
<div>
<textarea contenteditable id="id-description" name="description"></textarea>
</div>
{% endblock description %}
<input id="ok" type="submit">
</form>

Trying to access ModelForm field modelChoice choices in Django template

I'm generating ModelForms and want some granular control over how they are output in my template. Specifically, I need to add some markup to the end of each radio button in each of my select lists.
Code:
# order-form.html
{% load catname %}
<form id = "order-form">
{% for form in forms %}
<div id="gun-{{ forloop.counter }}">
{% for field in form.fields %}
<div id="{{ field }}-item" class="item">
<h3>{{ field|catname }}</h3>
{% for choice in form.field.choices %} {# <-- Help me out here #}
{{ choice.id }}
{{ choice.title }}
{% endfor %}
</div>
{% endfor %}
{% endfor %}
<button type="submit" id="standard-gun-form-submit">Continue to next step</button>
</form>
# views.py
def get_form(request):
if request.method == 'POST':
if request.POST['gun_type'] == 'standard':
forms = [StandardGunForm(prefix=p) for p in range(0,2)]
return render_to_response('main/order-form.html', {'forms' : forms,}, RequestContext(request))
# forms.py
class StandardGunForm(ModelForm):
def __init__(self, *args, **kwargs):
super(StandardGunForm, self).__init__(*args, **kwargs)
for field in self.fields:
if isinstance(self.fields[field], ModelChoiceField):
self.fields[field].empty_label = None
class Meta:
model = BaseGun
widgets = {
'FrameTuning' : RadioSelect(),
'FrameConnection' : RadioSelect(),
}
exclude = ('price')
Endgame: markup that looks like this
<form id="foo">
<div class="category">
<div class="item">
<input type="radio" name="srsbzns" value="1">Option 1</input>
<img src="http://placekitten.com/150/150">
<p>Other foo here</p>
</div>
<div class="item">
<input type="radio" name="srsbzns" value="2">Option 2</input>
<img src="http://placekitten.com/150/150">
<p>Other foo here</p>
</div>
<div class="item">
<input type="radio" name="srsbzns" value="3">Option 3</input>
<img src="http://placekitten.com/150/150">
<p>Other foo here</p>
</div>
</div>
</form>
From the shell, this returns what I want
>>> forms = [StandardGunForm(prefix=p) for p in range(0,2)]\
>>> forms[0].fields['frame_tuning'].choices.queryset
I'm surprised this is proving so challenging!
Bonus: I have DEBUG = True and Django Debug toolbar enabled. Is it possible to dump the variables to the browser, so I can see what this stuff looks like as I drill down?
Thanks!
I had to do something similar and started down this path as well. I wanted to create table rows from a ModelChoiceField where each column had a different field of the model instance (and then I'd allow filtering the table rows via JavaScript).
I couldn't find it in the Django docs, but a quick perusal of the Django source showed the way. You can get to the queryset to access the model instances like so:
<form action="{% url 'some_view' %}" method="post">
{% csrf_token %}
{% if form.non_field_errors %}
{{ form.non_field_errors }}
{% endif %}
{% for field in form %}
{{ field.label }}
{% if field.field.choices %}
{% for model_instance in field.field.choices.queryset %}
{{ model_instance.id }}
{% endfor %}
{% else %}
{{ field }}
{% endif %}
{% if field.errors %}
{{ field.errors|striptags }}
{% endif %}
{% endfor %}
<button type="submit">Submit</button>
</form>
However, at this point we've disassembled the shiny widget (in my case a CheckboxSelectMultiple) and must re-assemble the HTML form input using template code. I found no direct way to simultaneously iterate over the ModelChoiceField to access the model instance fields and get the HTML form tags for the choices.
Maybe there's a way, but I abandoned my attempt and built my own HTML form, handling all the POST data in a view. It ended up much easier that way. ModelForms are really nice and convenient, but using them for something they weren't built for can end up being more difficult.
I figured I'd post this in case anyone is trying to do it for some other reason. Hope it helps.
Very late, but I'm reading now and this is what it worked for me
{% for field in form %}
{% for x, y in field.field.choices %}
{{x}}
{{y}}
{% endfor %}
{% endfor %}
Where "x" is the id or code, and "y" is the readable value or title.
You can access the underlying model instance for each choice:
{% for choice, label in form.field_name.field.choices %}
{{ choice.value }}
{{ choice.instance }}
{{ choice.instance.instance_attribute }}
{{ label }}
{% endfor %}
{% for choice in form.field.choices %} {# <-- Help me out here #}
{{ choice.id }}
{{ choice.title }}
{% endfor %}
Look what you're doing here, you're literally trying to access a field called "field" every time in this loop, which presumably does not exist.
You need to take the field object you're iterating through, and access the choices attribute on that.
{% for field in form.fields %}
{% for choice in field.choices %}

Why is this django formset not being submitted?

i have a formset as follows:
EduFormSet = formset_factory(forms.CandidateDegreeForm, can_delete=True)
edu_formset = EduFormSet(prefix='candidate_degree')
in the templates i am doing the following:
{% if edu_formset %}
{% for form in edu_formset %}
<div class="formset-form" style="visibility: visible;">
<form id="{{ form.prefix }}" method="POST" action="/degree/add/">
<h4>Some Heading Here</h4>
{% csrf_token %}
{% for field in form %}
{% include "form_field.html" %}
{% endfor %}
</form>
<script type="text/javascript">
jQuery(document).ready ( function(){
jQuery('{{ form.prefix }}').validationEngine();
});
</script>
<div class="clearfix"></div>
</div>
{% endfor %}
{{ edu_formset.management_form }}
<div class="button-container right">
<input class="button" type="submit" value="submit" />
</div>
{% endif %}
I am not sure why but nothing really happens when i hit the submit button.
Your submit button is not within the form, so the action is not triggered by the click!
Here's how the docs show you to render formsets:
<form method="post" action="">
<!-- Notice how the formset (below) and thus its submit button
is INSIDE the form (above) -->
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>
You try to create multiple forms with the form.prefix for id. This could work but each form would have to be rendered with its own submit button. Formsets are designed to combine multiple forms into one and guarantee uniqueness of value names by said prefix. They would be enclosed in a singe form and share any submit triggers.