I overrided get_results method of autocomplete-light as follow to have single tag in bold:
views.py
class TagAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self, **kwargs):
qs = Tag.objects.all()
if self.q:
qs = qs.filter(name__istartswith=self.q)
return qs
def get_results(self, context):
results_list = []
for result in context["object_list"]:
text2show = "{}"
tag_occurrences = result.org_set.count()
if tag_occurrences == 1:
text2show = '<b>{}</b>';
results_list.append({
"id": self.get_result_value(result),
"text": format_html(text2show, self.get_result_label(result)),
"selected_text": format_html(text2show, self.get_selected_result_label(result)),
})
return results_list
Everything works perfectly if I search a tag, but if I try to view a stored event, all its tags are represented without any style.
forms.py
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = "__all__"
widgets = {
'tags': autocomplete.ModelSelect2Multiple(url='myapp:tag-autocomplete', attrs={'data-html': True})
}
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
# if I try to edit an event
if self.instance:
# self.fields['tag'].queryset = ???
And this is a piece of my template:
<form method="post" class="form-horizontal">
{% csrf_token %}
{% for field in myform %}
<div class="form-group form-group-lg">
<label for="{{ field.id_for_label }}" class="col-sm-4 control-label">{{field.label}}</label><div class="col-sm-4">
{{ field }}
</div>
</div>
{% endfor %}
{{ myform.media }}
</form>
Is there a way to manipulate self.fields['tag'].queryset queryset to show field names in bold or another elegant solution?
Thanks.
update
models.py
class Tag(models.Model):
name = models.CharField('Tag name', max_length=255)
class Meta:
ordering = ['name']
def __init__(self, *args, **kwargs):
return mark_safe("<b>%s</b>" % self.name)
def __str__(self):
return self.name
Related
I am trying to create a page where various data corresponding to mutliple models can be input by the user, and to have an option to dynamically add additional forms. I have been attempting to use htmx for this and am able to dynamically add forms, however when I save it is only the last entered form that is saved. I haven't used formsets as this wont integrate well with htmx https://justdjango.com/blog/dynamic-forms-in-django-htmx#django-formsets-vs-htmx.
Code below any suggestion as to how to get all the dynamically created forms to be save would be most appreciated!
models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
forms.py
from django import forms
from .models import Book, Author
from crispy_forms.helper import FormHelper
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = ['name']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-CaseForm'
self.helper.form_class = 'blueForms'
self.helper.form_method = 'post'
self.helper.form_tag = False
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ('title',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.form_tag = False
self.helper.form_show_labels = False
views.py
class create_book(TemplateView):
template_name = 'create_book.html'
def get(self, *args, **kwargs):
author_form = AuthorForm
book_form = BookForm
return self.render_to_response(
{'book_form': BookForm,
"author_form": AuthorForm,}
)
def post(self, *args, **kwargs):
author_form = AuthorForm(data=self.request.POST)
book_form = BookForm(data=self.request.POST)
if author_form.is_valid():
author = author_form.save()
if book_form.is_valid():
book = book_form.save(commit=False)
book.author = author
book.save()
def create_book_form(request):
form = BookForm()
context = {
"form": form
}
return render(request, "partials/book_form.html", context)
urls.py
urlpatterns = [
path('create_book/', create_book.as_view(), name='create-book'),
path('htmx/create-book-form/', create_book_form, name='create-book-form'),]
create_book.html
{% extends "base.html" %}
{% block content %}
{% load crispy_forms_tags %}
<form class="blueForms" id="id-CaseForm" method="post" >
{% crispy author_form %}
{% crispy book_form %}
<button type="button" hx-get="{% url 'create-book-form' %}" hx-target="#bookforms" hx-swap="beforeend">
Add book form
</button>
<div id="bookforms"></div>
</form>
partials/book_form.html
{% load crispy_forms_tags %}
<div hx-target="this" hx-swap="outerHTML">
{% csrf_token %}
{% crispy form %}
{{ form.management_form }}
</div>
A quick Google finds me this module:
https://github.com/adamchainz/django-htmx
It seems someone may have already done this for you my friend :)
I have used as many examples online as I could cobble together in an attempt to get my two simple models to have the ability to do an inline formset allowing me to add many files to a technial drawing.
This is not working, I can only add one file for the Create and the update only updates the Technical_Entry model, not a file...which in of itself acts funny. The UI ona create shows one spot to add a file, then save the entire record and its child record. That works.
The update, the UI shows the file that was saved earlier..(great!) but then has two more 'choose file' slots (random?) and adding a file to those does nothing when the save is clicked. It doesn't remove the file previously added in the create, but it also
does not save the new file added to the now three slots (one used, two free). So update does not work for the files for some reason.
class Technical_Entry(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
ema = models.ForeignKey(EMA, on_delete=models.CASCADE)
system = models.ForeignKey('System', on_delete=models.CASCADE) # are SYSTEMS RELATED TO SUBSYSTEMS OR JUST TWO GROUPS?
sub_system = models.ForeignKey(SubSystem, on_delete=models.CASCADE)
drawing_number = models.CharField(max_length=200)
drawing_title = models.CharField(max_length=255)
engineer = models.CharField(max_length=200)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
date_drawn = models.DateField()
ab = models.BooleanField()
class Technical_Entry_Files(models.Model):
tech_entry = models.ForeignKey(Technical_Entry, on_delete=models.CASCADE)
file = models.FileField(upload_to='techdb/files/')
def __str__(self):
return self.tech_entry.drawing_number
To upload using a formset. While the page 'displays' mostly correctly, it flat out does not create the record in the Technical_Entry_Files model.
Relevant forms.py:
class FileUploadForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FileUploadForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
class Meta:
model = Technical_Entry_Files
fields = ('file',)
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=1, max_num=15)
class Technical_EntryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Technical_EntryForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-4'
self.helper.field_class = 'col-lg-8'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = Technical_Entry
fields = ('category', 'ema', 'system', 'sub_system', 'drawing_number', 'drawing_title', 'engineer', 'vendor', 'date_drawn', 'ab')
widgets = {
'date_drawn':DateInput(attrs={
'class':'datepicker form-control',
'id' : 'datetimepicker2',
'tabindex' : '1',
'placeholder' : 'MM/DD/YYYY hh:mm',
'autocomplete':'off',
}, format='%m/%d/%Y'),
'system' : Select(attrs={'tabindex':'2'}),
}
Relevant views.py:
class TechEntryCreateView(LoginRequiredMixin, CreateView):
print ("we are here")
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
print(template_name)
success_url = '/techentry_add'
def get_context_data(self, **kwargs):
data = super(TechEntryCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['file_upload'] = TechFileFormSet(self.request.POST, self.request.FILES)
else:
data['file_upload'] = TechFileFormSet()
return data
def form_valid(self, form):
context =self.get_context_data()
file_upload = context['file_upload']
with transaction.atomic():
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryCreateView, self).form_valid(form)
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
return super(TechEntryUpdateView, self).form_valid(form)
and the tech_entry_form.html:
{% extends 'base.html' %}
{% load static %}
{% block page-js %}
<script>
$('.link-formset').formset({
addText: 'add file',
deleteText: 'remove',
});
</script>
{% endblock %}
{% block content %}
<main role="main" class="container">
<div class="starter-template">
<h1>New Tech Entry</h1>
</div>
<h2> Details of Technical Entry </h2>
<div class="row">
<div class="col-sm">
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<h2> Files </h2>
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
<input type="submit" value="Save"/>back to the list
</form>
</div>
</div>
</div>
</main><!-- /.container -->
{% endblock %}
And what the UI looks like on edit...
class TechEntryUpdateView(LoginRequiredMixin, UpdateView):
model = Technical_Entry
form_class = Technical_EntryForm
template_name = 'techdb/tech_entry_form.html'
success_url = '/'
#log_entry_class = Technical_EntryForm(Technical_Entry) #removed
def get_context_data(self, **kwargs):
context = super(TechEntryUpdateView, self).get_context_data(**kwargs)
#self.object = self.get_object() #removed
if self.request.POST:
context["file_upload"] = TechFileFormSet(self.request.POST, self.request.FILES,instance=self.object)
else:
context["file_upload"] = TechFileFormSet(instance=self.object)
return context
def form_valid(self, form):
context = self.get_context_data()
file_upload = context["file_upload"]
self.object = form.save()
if file_upload.is_valid():
file_upload.instance =self.object
file_upload.save()
#return super().form_valid(form)
return super(TechEntryUpdateView, self).form_valid(form) #replaced old one
UPDATE
1. in order to be able to add mulitple files when creating,
TechFileFormSet = inlineformset_factory(Technical_Entry, Technical_Entry_Files, form=FileUploadForm, extra=4, max_num=4)
# note I changed to extra=4, if you always want to have only 4 files, then also change to max_num=4
2. When update, the reason why the update is not working even after modifying the views was because you were not passing hidden fields. You were not passing the ids of the files, therefore, your formsets were not passing .is_valid(), hence, no update. Add for loop about hidden fields like below.
{{ file_upload.management_form }}
{% for upload_form in file_upload.forms %}
{% for hidden in upload_form.hidden_fields %}
{{hidden}}
{% endfor %}
<div class="link-formset">
{{ upload_form.file }}
</div>
{% endfor %}
#Note the for loop I add about hidden fields.
I have 3 models one is Category(Fields = category_name) and another one is SubSategory(Fields = category(ForeignKey to Category),sub_category).and another model is DummyModel.
# Model
class DummyModel(models.Model):
name = models.CharField(max_length=20)
email = models.EmailField()
category = models.ManyToManyField(Category)
sub_category = models.ManyToManyField(SubCategory)
This is my form
class StartProjectForm(ModelForm):
class Meta:
model = StartProject
fields = (
'name',
'email',
'category',
'sub_category',
)
def __init__(self, *args, **kwargs):
super(StartProjectForm, self).__init__(*args, **kwargs)
self.fields["category"].widget = CheckboxSelectMultiple()
self.fields["category"].queryset = Category.objects.all()
self.fields["sub_category"].widget = CheckboxSelectMultiple()
self.fields["sub_category"].queryset = SubCategory.objects.all()
def save(self, commit=True):
clean = self.cleaned_data.get
name = clean('name')
email = clean('email')
category = clean('category')
sub_category = clean('sub_category')
obj = StartProject()
obj.name = name
obj.email = email
obj.category = category
obj.sub_category = sub_category
obj.save()
This is my view
#view
class StartProjectView(View):
template_name = 'start-project.html'
def get(self, request):
form = StartProjectForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = StartProjectForm(request.POST)
if form.is_valid():
form.save()
form = StartProjectForm()
return render(request, self.template_name, {'form':form})
return HttpResponse("<h2>Done</h2>")
This is my Template
# Template
<form method="post">
{% csrf_token %}
<p>name: <input type="text" name="name"></p>
<p>Email: <input type="text" name="email"></p>
{% for form in form %}
<input type="checkbox" name="category">{{ form.category }}
{% endfor %}
<br>
{% for form in form %}
<input type="checkbox" name="sub_category">{{ form.sub_category }}
{% endfor %}
<button type="submit">Start Now</button>
</form>
I want category and subcategory in my template as checkbox items. How do I do that.?
After digging in your needs, what you are looking for is {{ form.FIELD_NAMEĀ }}.
Whit your form {{ form.category }} and {{ form.sub_category }} should work.
Take into account that this only renders the input itself, nor labels or other DOM elements.
Review the docs on 'Rendering fields manually' for more info -> https://docs.djangoproject.com/en/2.2/topics/forms/#rendering-fields-manually
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
Radio-buttons are not showing, its the check-boxes displayed in the Class Based Views. I want them to be showing the radio-buttons
forms.py
class ProductImagesForm(forms.ModelForm):
media = forms.ImageField(label='Image')
def __init__ (self, *args, **kwargs):
super(PerstransForm, self).__init__(*args, **kwargs)
self.fields['featured_image'] = forms.BooleanField( widget = forms.RadioSelect(choices=((self.prefix, 'featured'),))
def add_prefix(self, field):
if field == 'featured_image': return field
else: return self.prefix and ('%s-%s' % (self.prefix, field)) or field
class Meta:
model = ProductImages
fields = ['media', 'featured_image']
ImagesFormset = modelformset_factory(ProductImages, fields=('media', 'featured_image'), extra=1)
models.py
def product_download(instance, filename):
return '%s/%s' %(instance.product.slug, filename)
class ProductImages(models.Model):
product = models.ForeignKey(Product)
title = models.CharField(max_length=120)
media = models.ImageField(upload_to=product_download,
width_field='max_width',
height_field='max_height',
null=True, blank=True)
max_width = models.CharField(max_length=100, null=True, blank=True)
max_height = models.CharField(max_length=100, null=True, blank=True)
featured_image = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __unicode__(self):
return unicode(self.media)
class Meta:
verbose_name = "product image"
verbose_name_plural = "product images"
Views.py
def get(self, *args, **kwargs):
self.object = self.get_object()
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 = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(self.request.POST, self.request.FILES)
form_valid = form.is_valid()
formset_valid = formset.is_valid()
if form_valid and formset_valid:
seller = self.get_account()
form.instance.seller = seller
self.object = form.save()
media = formset.save(commit=False)
for img in media:
img.product = self.object
img.save()
formset.save()
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)`
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">
{{ formset }}
{% for choice in formset.featured_image %}
<ul>
<li><input type="radio" name="featured"></li>
</ul>
{% endfor %}
</div>
<input type="submit" value="{{ submit_btn }}">
</form>
Hope someone can help me to fix in the missing pieces
This will happen because you choose BooleanField that means a specific selection is either 0 or 1 and radio button is used to select one option from multiple choices hence you should try using forms.ChoiceField instead of forms.BooleanField.
for more details read this official django doc
I have got to some point and I guess it will help some folks
template
{{ formset.management_form }}
<div class="link-formset">
{% for choice in formset %}
<div>
{{ choice.media }}
<input type="radio" name="{{choice.featured_image.label}}">{{ choice.featured_image.label }}</
</div>
{% endfor %}
</div>