Crispy forms use the string representation of objects provided by the str method of the objects' class: I need to change this behaviour (please, help me).
In my crispy form the labels of the choices in a CheckboxSelectMultiple() field populate from the default str method of the objects represented. The set of objects is defined in a list containing ids, with those ids crispy calls the str methods.
Is it possible to write a custom string representation (for instance as a class' #property) and tell crispy to use that instead?
If yes, which point in the pipeline would give the best programmer's practice (models/views/forms/template) ?
this image is just a dummy example for better illustrating the problem
Overriding the default str method provides the desired labelling (as suggested in this post) but is totally unacceptable because of side effects.
models.py
class School(models.Model):
nice_name_for_forms = models.CharField(max_length=100)
#property
def label_from_instance(self):
return '%s' % (self.nice_name_for_forms)
views.py
school_list = School.objects.all().values_list('id', flat=True)
form = MyForm(request.POST, school_list=school_list)
forms.py
class MyForm(forms.ModelForm):
class Meta:
model = MyForm
fields = '__all__'
labels = {'schools' : 'My Schools'}
widgets = {'schools' : forms.CheckboxSelectMultiple()}
def __init__(self, *args, **kwargs):
self.school_list = kwargs.pop('school_list')
super().__init__(*args, **kwargs)
self.fields['schools'].queryset = self.fields['schools'].queryset.filter(id__in=self.school_list).distinct()
self.helper = FormHelper()
self.helper.use_custom_control = False
self.helper.layout = Layout(
Row(CheckboxAllFieldCompact('schools', wrapper_class='col-4 col-md-2'))
checkbox_all_field.html
<!-- crispy/checkbox_all_field.html -->
{% load crispy_forms_field %}
{% load i18n %}
<div id="div_checkbox_all_{{ field.html_name }}" class="no-form-control control-group {{ wrapper_class }}">
<div class="controls" style="max-height:250px;overflow:auto">
<label for="{{ field.name_for_label }}" class="label_title inline">{{ field.label }}</label>
<br />
<label class="block">
<button id="check_all_{{ field.html_name }}" type="button" class="btn btn-default btn-sm" actif="false">{% translate 'Select all' %}</button>
</label>
{% crispy_field field %}
</div>
</div>
Related
template
<form method="get">
{{ filter.form.as_p }}
<input type="submit" value="Press" />
</form>
{% for obj in filter.qs %}
{{ obj.WorkType }} - ${{ obj.desired_wage }}<br />
{% endfor %}
views
#login_required(login_url='login')
def ListingsPage(request):
review = Review.objects.all()
filter = Profilefilter(request.GET, queryset=Profile.objects.all())
context = {"profile":profile,"review":review,"filter":filter}
return render(request,"base/Listings.html",context)
filters.py
import django_filters
from .models import Profile
class Profilefilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='iexact')
class Meta:
model = Profile
fields = ['WorkType', 'gender']
urls.py
urlpatterns = [
path('Listings/', views.ProfileSearch, name='profile_search_bar'),
path('',views.hello,name="home"),
path('Listings/', views.ListingsPage,name="listings"),
It is supposed to be showing the filters but doesn't render anything, only the submit button shows up. I think it is something to do with passing the context, not sure tho
You need to add qs for query filter
def ListingsPage(request):
review = Review.objects.all()
filter = Profilefilter(request.GET, queryset=Profile.objects.all())
review = filter.qs
context = {"profile":profile,"review":review,"filter":filter}
return render(request,"base/Listings.html",context)
After struggling with this issue for a while, I am hoping someone here can point me in a more productive direction.
I am trying to take an indeterminate number of variables in a database (obtained through a different template) and render them on a webpage, each variable with a simple data entry form, to be saved back to the database. Basically, it's a tracker for analysis. Say I want to track my daily sleep, running time, and calorie intake (the variables). I have those saved in a database as variables and want to call upon those variables and show them on a webpage with a daily entry form. I am using a "for" loop right now and it renders the way I want it to, with the variable name and the form, but it is only saving the last item in the variable list. How do I amend the code below such that when I hit the save button for each form rendeded, it saves the information for that variable (not just the last one rendered).
Below is the code. Any and all help would be infinitely appreciated.
Models...
class Variable(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
ENTRY_TYPE_CHOICES = [
('numeric', 'enter a daily number'),
('scale', 'rate daily on a scale of 1-10'),
('binary', "enter daily, 'yes' or 'no' "),
]
variable = models.CharField(max_length=50, default='')
entry_type = models.CharField(max_length=50, choices=ENTRY_TYPE_CHOICES, default="numeric")
def __str__(self):
return self.variable
class DailyEntry(models.Model):
date = models.DateField(default=datetime.date.today)
# date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
variable_name = models.CharField(max_length=50, default='')
variable_id = models.SmallIntegerField(default=0000)
entry = models.FloatField(default=9999)
class Meta:
verbose_name_plural = 'Daily Entries'
def __str__(self):
return self.variable
Form...
class VariablesForm(forms.ModelForm):
class Meta:
model = Variable
fields = ['variable', 'entry_type' ]
labels = {'variable':'Dependent variable to track', 'entry_type': 'Type of measure'}
class DailyEntryForm(forms.ModelForm):
class Meta:
model = DailyEntry
fields = ['variable_name', 'variable_id', 'entry', 'date']
labels = {'entry': 'Daily entry', 'date': 'Date'}
widgets = {'variable_name': forms.HiddenInput(), 'variable_id': forms.HiddenInput()}
Views...
def daily_entry(request):
''' page to make daily entries '''
vars = Variable.objects.filter(id__gt = 0 )
if request.method != 'POST':
# No data submitted. GET submitted. Create a blank form
form = DailyEntryForm()
else:
#POST data submitted. Process data
form = DailyEntryForm(data=request.POST)
if form.is_valid():
data = form.save(commit=False)
data.created_by = request.user
data.save()
return HttpResponseRedirect(reverse('entry_new'))
context = {'form': form, 'vars': vars}
return render(request, 'entry_new.html', context)
and HTML...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% for var in vars %}
<div>
<ul>
<h3>{{ var.variable }}</h3>
<form class="" action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="variable_id" value="{{ var.id }}" >
<input type="hidden" name="variable_name" value="{{ var.variable }}">
<input type="submit" name="" value="Save" />
</ul>
</div>
{% endfor %}
{% endblock content %}
Any help, well, helps...
Thanks!
I have a TextField intended to store a large amount of text that can be logically split into 10 parts. I thought that it would make sense to create 10 separate Textareas, each for one logical part. Thus, I subclassed MultiWidget and MultiValueField like so:
class MultiWidget(forms.widgets.MultiWidget):
template_name = "custom_content_widget.html"
attrs = {"class": "textarea form-control"}
def __init__(self, attrs=None):
widgets = [Textarea()] * 10
super(MultiWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return value
return ["", "", "", "", "", "", "", "", "", ""]
class ContentField(MultiValueField):
widget = MultiWidget
def __init__(self, *args, **kwargs):
# Define one message for all fields.
error_messages = {
'required': 'This field is required.',
}
# Or define a different message for each field.
fields = [CharField()] * 10
super(ContentField, self).__init__(
error_messages=error_messages, fields=fields, require_all_fields=True, *args, **kwargs)
# self.helper = FormHelper()
# self.helper.layout = Layout(
#
# )
def compress(self, data_list):
return " ".join(data_list)
with the custom_content_widget.html being just
{% for subwidget in widget.subwidgets %}
{% with widget=subwidget %}
{% include widget.template_name %}
{% endwith %}
{% endfor %}
Simple model and form in which I'd like to use this multiwidget
class Opinion(models.Model):
content = models.TextField()
class OpinionForm(forms.ModelForm):
content = ContentField()
class Meta:
model = Opinion
fields = ('__all__')
The problem is that when I use content in my form's HMTL as {{ form.content | as_crispy_field }} it renders pretty ugly
and I'd like all of the Textareas to be rendered one under the other. The main issue here is that textarea is rendered as
<textarea name="content_0" cols="40" rows="10" class="textarea" required id="id_content_0">
</textarea>
while "normal" TextField is rendered as
<textarea name="content" cols="40" rows="10" class="textarea form-control" required id="id_content">
</textarea>
and I have no clue how could I force the class of the widget to be textarea form-control instead of textarea. Initially, I found this question and also this blog but all they do is just to properly group widgets into rows and columns. Is there anything I am missing here?
The key was to pass attributes dict with "class": "textarea form-control" to the MultiWidget constructor as follows
class ContentField(MultiValueField):
widget = MultiWidget({"class": "textarea form-control"})
def __init__(self, *args, **kwargs):
# Define one message for all fields.
error_messages = {
'required': 'This field is required.',
}
# Or define a different message for each field.
fields = [CharField()] * 10
super(ContentField, self).__init__(
error_messages=error_messages, fields=fields, require_all_fields=True, *args, **kwargs)
def compress(self, data_list):
return " ".join(data_list)
I have made this HTML code:
<h3>your major is {{user.userprofile.major}}</h3>
This will correctly show the major on the webpage, but I want to use this string to get something from another table in view.
How would I pass this string to view?
edit:
Here is my view.py
def dashboardView(request):
obj = BooksFile.objects.all()
query = BooksFile.objects.filter(book_major='cs)
return render(request, 'dashboard.html', {'books': obj, 'major': query})
def registerView(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
profile_form = UserProfileForm(request.POST)
if form.is_valid() and profile_form.is_valid():
user = form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
return redirect('login_url')
else:
form = UserCreationForm()
profile_form = UserProfileForm()
context = {'form': form, 'profile_form': profile_form}
return render(request, 'registration/register.html', context)
here is my template:
{% extends 'index.html' %}
{% block content %}
<h1>Welcome, {{user.username}}</h1>
<h2>Your major is {{user.userprofile.major}}</h2>
{% for book in books %}
<h3>Your book name is {{book.book_name}}</h3>
{% endfor %}
{% endblock %}
I am trying to show the book names from the booksfile table by corresponding major that user has. Right its showing the books that has "cs" attribute because I manually put "cs" in the get function in view. I am trying to send the major string from template to view, so that I can put what ever the user's major is in the get function. Or is there any other way to do it.
You need to use a form in your template and submit it to call your view. i.e.
<form action="your_view_url" method="POST">
<input type="text" name="major" value="{{user.userprofile.major}}"/>
<input type="submit"/>
</form>
an then in your view you access that with:
if request.POST:
major = request.POST.get('major')
As per documentation: https://docs.djangoproject.com/en/2.2/topics/forms/
First of all you have to get the value of model with help of queryset, and put it in the dictionary and then pass it with the template.
In views:
def get(self, request):
queryset = Model_name.objects.all()
ctx = {
'queryset': queryset,
}
return render(request, 'page_name(or template_name).html', ctx)
in template:
<form action="{%url'(your_view_name without brackets)'%}" method="POST">
{% for data in queryset%}
<span class="username">{{data.name(field of your model)}} .
</span>
<span class="email">{{data.email(field of your model)}} .
</span>
{% endfor%}
</form>
I would like to filter data in Django (admin.py) with text writen in HTML input textbox. I need to filter companies by city in which they are and list of all cities is too long. I would like to replace list of all cities in filter by one text input. I found something similar
here http://djangosnippets.org/snippets/2429/ but there are two problems:
author did not posted models.py, so it is difficuilt to change code for my needs (+ no comments)
there is used class UserFieldFilterSpec(RelatedFilterSpec): but I need to use AllValuesFilterSpec instead of RelatedFilterSpec (more in file django/contrib/admin/filterspecs.py), because list of towns are in the same class as comapny (there shoud by class of towns and they should be referencing to company by foreign key (ManyToMany relationship), but for some reasons it have to be done this way)
important part of models.py looks something like this
class Company(models.Model):
title = models.CharField(max_length=150,blank=False)
city = models.CharField(max_length=50,blank=True)
and something from admin.py
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = ['city',]
So again, I need to:
1. instead of list od cities display one text input in Django filter
2. After inputing city neme in that text input, filter data by city (request for filtering can be sent with some submit button or through javascript)
Thank yoy for all posts.
In case anybody still need this. It is little hackish in template, but implemented without a piece of js.
filters.py:
from django.contrib.admin import ListFilter
from django.core.exceptions import ImproperlyConfigured
class SingleTextInputFilter(ListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, request, params, model, model_admin):
super(SingleTextInputFilter, self).__init__(
request, params, model, model_admin)
if self.parameter_name is None:
raise ImproperlyConfigured(
"The list filter '%s' does not specify "
"a 'parameter_name'." % self.__class__.__name__)
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html:
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Then according to your models in admin.py:
class CatalogCityFilter(SingleTextInputFilter):
title = 'City'
parameter_name = 'city'
def queryset(self, request, queryset):
if self.value():
return queryset.filter(city__iexact=self.value())
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = [CatalogCityFilter,]
Ready to use filter would look like this.
I'm running Django 1.10, 1.11 and r_black's solution didn't completely fit because Django was complaining that filter fields must inherit from 'FieldListFilter'.
So a simple change for the filter to inherit from FieldListFilter took care of Django complaining and not having to specify a new class for each field, both at the same time.
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
if self.value():
return queryset.filter(imei__icontains=self.value())
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html (unchanged):
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Usage:
class MyAdmin(admin.ModelAdmin):
list_display = [your fields]
list_filter = [('field 1', SingleTextInputFilter), ('field 2', SingleTextInputFilter), further fields]
While it's not actually your question, this sounds like a perfect solution for Django-Selectables you can with just a few lines add an AJAX powered CharField Form that will have it's entries selected from the list of cities. Take a look at the samples listed in the link above.
Below is the fix for field name..in queryset function
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
variable_column = self.parameter_name
search_type = 'icontains'
filter = variable_column + '__' + search_type
if self.value():
return queryset.filter(**{filter: self.value()})
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': ('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )