I'm rendering a form in a django template using this model:
class Group(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
members = models.IntegerField(default=0)
has_max = models.BooleanField(default=False)
max_size = models.IntegerField(default=10)
DAYS = [
('Sundays', 'Sundays'),
('Mondays', 'Mondays'),
('Tuesdays', 'Tuesdays'),
('Wednesdays', 'Wednesdays'),
('Thursdays', 'Thursdays'),
('Fridays', 'Fridays'),
('Saturdays', 'Saturdays'),
]
meeting_day = MultiSelectField(
verbose_name = 'Meeting day(s)',
choices=DAYS,
max_choices=6,
max_length=100
)
start_time = TimeField(widget=TimePickerInput)
end_time = TimeField(widget=TimePickerInput)
def __str__(self):
return self.name
And onto this template:
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button>POST</button>
</form>
I have a couple of issues going forward:
My first issue is that I only want to have max_size be displayed in my template form if the user clicks has_max. Like a conditional, where once the user checks the box, changing has_max to True then they can enter in the max size of the group.
My second issue is that the start_time and end_time to render in my template or on the admin side. I'm not sure how TimePickerInput works, but right now, there are no fields in my template form or admin form that have those time fields.
Also, last thing, if I have both names the exact same (i.e., ('Sundays', 'Sundays'), is it necessary to have both? Or can django figure it out.
The first Problem as I understand it You want to have this check box and when it will true the Field of max_size will appear this problem needs to solve with javascript
Why ?
Because if You use the Django template you will need to refresh the page when the user changes something and this will overload the server So you need to create an event with js when the use click on the checkbox it will disappear
Also, last thing, if I have both names the exact same (i.e., ('Sundays', 'Sundays'), is it necessary to have both? Or can Django figure it out?
This part ... You need to know that the first value is the value will be stored in the database and the second value is the human-readable value this what will be displayed for the user in the form Read This
You must know you can't put widgets into your model if you want to add it add it to your from in Your admin or form files ... You will override the AdminModel in your admin file and change the formvalue Read This
Related
I am trying to create a multiple choice form where any combination of languages can be chosen. It's within a search form field:
class AdvancedSearchForm(SearchForm):
terms_show_partial_matches = forms.BooleanField(required=False,
label=_("Show partial matches in terms")
)
definitions_show_partial_matches = forms.BooleanField(required=False,
label=_("Show partial matches in definitions")
)
case_sensitive = forms.BooleanField(required=False,
label=_("Case sensitive")
)
...
I would like to implement something like this:
filter_by_part_of_speech = forms.ModelChoiceField(
queryset=PartOfSpeech.objects.all(), required=False,
label=_("Filter by part of speech")
)
However, it needs to be a multiple choice field so that any of the values can be chosen. Ideally though, I'm looking for a form where checkboxes are already checked. So something like this:
LANG_CHOICES = (
("1", "lang1"),
("2", "lang2"),
("3", "lang3"),
("4", "lang4"),
)
filter_by_language = forms.MultipleChoiceField(choices=Language.objects.all().filter(name__in=LANG_CHOICES).values(), required=False, label=_("Filter by language"))
The filter is called from the view with something like this:
tqs = tqs.filter(language=language_filter)
Now although the search works fine, the values are not displayed. On the other hand, they are displayed if I fill up a list and simply write:
choices=list(lang_list)
But then, obviously, the search is not actually performed.
Therefore, my questions are:
Can the constructor be adapted to display the values correctly?
Should I rather implement the filter in the view? If so, how?
Am I using the correct type of form or are there better options, such as providing a list of checkboxes that are checked by default?
I am using Django 2.2 (planning to upgrade soon) for now.
The template file simply refers to the search def in the view, which calls the advanced search form and the others:
{% block breadcrumbs_item %}{% trans "Advanced Search" %}{% endblock %}
Not that relevant I think, but here is the Language model:
class Language(models.Model):
iso_code = models.CharField(primary_key=True, max_length=10, verbose_name=_("ISO code"))
name = models.CharField(max_length=50, verbose_name=_("name"))
description = models.TextField(verbose_name=_("description"), null=True, blank=True)
class Meta:
verbose_name = _("language")
verbose_name_plural = _("languages")
def __str__(self):
return _("%(language_name)s (%(iso_code)s)") % {'language_name': self.name, 'iso_code': self.iso_code}
EDIT: Clarification based on Milo Persic's reply.
The request method in the view for the search functionality (that includes calling AdvancedSearchForm) is GET.
if request.method == 'GET' and 'search_string' in request.GET:
if "advanced" in request.path:
search_form = AdvancedSearchForm(request.GET)
else:
search_form = SearchForm(request.GET)
if search_form.is_valid():
tqs = Translation.objects.all()
dqs = Definition.objects.all()
data = search_form.cleaned_data
...
language_filter = data['filter_by_language']
case_filter = data['case_sensitive']
qs = apply_filters(tqs, dqs, language_filter, case_filter, orig_search_string)
etc.
The search form and results are appended and returned as context to the template:
return render(request, template_name, context)
Within AdvancedSearchForm, variables using ModelChoiceField as well as BooleanField are defined. The ModelChoiceField is the one I'm trying to replace with something more user friendly:
filter_by_language = forms.ModelChoiceField(
queryset=Language.objects.all(), required=False,
label=_("Filter by language")
)
The idea is that the default search result will include all four languages (it won't be more than that) but that any combination of them can be unchecked if so desired. With ModelChoiceField, it seems that only one can be chosen.
I'm still trying to learn if ModelMultipleChoiceField is the correct choice, but this is what I have so far:
filter_by_language = forms.ModelMultipleChoiceField(required=True, queryset=Language.objects.all().filter(name__in=LANG_CHOICES).values(),
widget=forms.CheckboxSelectMultiple)
Nothing next to "filter by language".
Only using the names in LANG_CHOICES instead of the numbers actually result in something but it shows the query set. Narrowing it down using "value_list('name')" shows only the language name but still with the syntax, e.g. "('English',)".
mmcf_qs = Language.objects.all().filter(name__in=LANG_CHOICES).values_list('name')
filter_by_language = forms.ModelMultipleChoiceField(required=True, queryset=mmcf_qs)
I'm trying to figure out how to extract the text but maybe this isn't the right approach.
I would use something like CheckBoxSelectMultiple in the form:
(disclaimer, I do not know if this is supported as written in django 2.2)
filter_by_language = forms.ModelMultipleChoiceField(required=True,
queryset=Language.objects.all().filter(name__in=LANG_CHOICES).values(),
widget=forms.CheckboxSelectMultiple)
And there are plenty of stack posts on how to make the initial values checked, like this one.
Also, if you end up with a long list of check boxes (a likely scenario using this widget), you can style them into columns for better UX, like so:
<style>
.my-language-checkboxes {
column-width: 25%;
column-count: 4;
column-fill: balance;
}
</style>
<div id="my-language-checkboxes">
{{ form.filter_by_language }}
</div>
As for the view, I'll make an assumption that you know how to write a basic view and it's the save method you need. In that case, you'll need something like:
if request.method == 'POST:
if form.is_valid():
form.save_m2m()
This also assumes that you are relating languages back to a parent object via a ManyToManyField, which is a common scenario.
Good day SO. I am trying to use the get_FOO_display on template but my data is not displaying. Also, I have read other SO answers and I believe that I followed them to a T but not working. Please try to correct me what I do wrong.
How I declared:
Models:
GENDER_CHOICES = (
(1, '男性'),
(2, '女性'),
)
gender = models.PositiveSmallIntegerField(choices=GENDER_CHOICES, default=0, null=False)
forms:
...
self.fields['gender'].widget.attrs.update({'class': 'form-control'})
view:
account = AccountViewForm(initial = {"gender" : account.gender})
template:
{{account.get_gender_display}}
If I only use {{account.gender}}, it becomes a select tag with the correct data. I want to display this as a TEXT tag.
Note. I also cant do <input type="text" value="{{account.gender}}" id="id_gender"> manually since this messes up the template.
It's because you're passing the .gender property, you need the whole model instance that contains the .gender attribute (Account model?)
the .get_FOO_display exists on the model as whole, not the field.
pass the account object in the context and use account_obj.get_foo_display()
I will give my models first and then write description.
class Entry(models.Model):
entry_text = models.TextField()
class Category(models.Model):
user = models.ForeignKey(User)
category_text = models.CharField(max_length=200)
entries = models.ManyToManyField(Entry, through='CategoryEntry')
class CategoryEntry(models.Model):
category = models.ForeignKey(Category)
entry = models.ForeignKey(Entry)
viewed = models.BooleanField(default=False)
So I have Entry model and Category model, and I have created intermediate model CategoryEntry as descriebed here https://docs.djangoproject.com/en/1.7/topics/db/models/#extra-fields-on-many-to-many-relationships because I need one extra field "viewed" (marked as True when user for the first time opens specific Entry link).
So I have created generic.ListView view, where I show all these categories that user has created for himself. What I want, is to show next to every category name, how many entries there are and how many entries he hasn't viewed yet.
Like:
Category Total Not_viewed
AAA 126 5
BBB 17 15
I have managed to show total entries in template by
{% for category in categories %}
{{ category.text }}
{{ category.entries.count }}
{% endfor %}
In my view I have get_queryset like
def get_queryset(self):
categories = Category.objects.filter(user=self.request.user.id)[:]
return categories
As I understand, then the best way would somehow add this extra info about every categories entries viewed count in get_queryset. I have searched around but didn't found anything what works. Have tried some things with select_related, prefetch_related, annotate but don't get whats the right way to do this.
Know that it's not right, but tried something like that and some other things.
categories = Category.objects.filter(user=self.request.user.id).select_related('categoryentry').filter(categoryentry__viewed=False).count()
categories = Category.objects.filter(user=self.request.user.id).annotate(not_viewed_count=Count('categoryentry')).filter(not_viewed_count__viewed=False)
Hope you get my idea what I wan't to achieve.
In your CategoryEntry model, use related_name in the category field like so:
category = models.ForeignKey(Category, related_name="related_entry_categories")
Now you can use this related name when querying the Category model. For example:
from itertools import chain
categories_not_viewed = Category.objects.filter(user=self.request.user.id, related_entry_categories__viewed=False).annotate(num_not_viewed=Count('related_entry_categories'))
categories_viewed = Category.objects.filter(user=self.request.user.id, related_entry_categories__viewed=True).extra(select={'num_not_viewed': 0})
categories = chain(list(categories_not_viewed), list(categories_viewed))
At end I came up with this solution:
categories = Category.objects.filter(user=self.request.user.id).extra(select = {
"num_not_viewed" : """
SELECT COUNT(*)
FROM app_categoryentry
WHERE app_categoryentry.category_id = app_category.id
AND app_categoryentry.viewed = %d """ % 0,
})
Based on the solution from this resource http://timmyomahony.com/blog/filtering-annotations-django/
If anyone have other solution how the get the same result with only Django ORM, I would like to know.
Having the following models:
#models.py
Column(models.Model):
name = models.CharField()
Input(models.Model):
column = ForeignKey(Column)
text = models.CharField()
And the following form:
class InputForm(Modelform):
class Meta():
model=Input
The following works:
c = Column.objects.get(...)
i=InputForm({'column':c.id})
i.is_valid() #true
In my application I am generating many forms, to avoid clashes I prefix it:
i=InputForm({'column':c.id}, prefix=prfx()) #prfx() is dynamically generated
i.errors # ({'column':['This field is required']})
i.data['column'] is still the right value
I also tried:
i.column = c
i.errors # ({'column':['This field is required']})
How do I populate the column field?
I cannot save the form as long as it does not validate
EDIT What I am trying to achieve:
I am building dynamic forms:
form_list = [InputForm(column, prefix=column.id) for column in col_list]
In the template I iterate over this form list (and I want to set the column field to be invisible.
{% for form in form_list %}
{{form.as_ul}}
{%endfor%}
This form then shall be processed by an AjaxView. The relation between text and column is achieved by the invisible field.
The first parameter to the form is the input data, which is usually request.POST. If you render a Form with a prefix, you will see that all form html elements will have a prefixed name, for example <input name='yourprefix_text' /> etc. If you POST such a form to your Django app, the POSTed data will have the prefix, too.
So, if you are using a prefix, the input data needs to be prefixed, too:
f = InputForm({'yourprefix_column': c.id}, prefix='yourprefix')
Usually, it is a better idea to use the initial parameter to the form for default values, because otherwise the form is always bound, and this has some consequences, for example default/initial values for other fields are will not work.
f = InputForm(prefix='yourprefix', initial={'column': c})
# or ...
form_list = [InputForm(prefix=column.id, initial={'column': column})
for column in col_list]
If you want to always set the column to a programatically determined value, and not allow the user to change it, it is better to not include the field in your form, and set the field manually after saving the object:
f = InputForm(request.POST)
if f.is_valid():
instance = f.save(commit=False)
instance.column = c
instance.save()
To make your field hidden, you can change the widget, as described in the documentation:
class InputForm(ModelForm):
class Meta:
widgets = {
'column': forms.HiddenInput,
}
# ...
I have a ModelForm class in which I set a couple of the fields as ChoiceField. For one of my views, I'd like to create a form from my ModelForm class that pulls from an instance of my model in the database (like so):
form = MyModel(instance=model_instance)
When I do this and then render the form in a template, I've noticed that most of the fields are pre-populated with values pulled from the model instance, which is what I want. However, this isn't the case for two ChoiceField fields. These render as drop-down select menus with no specific option selected.
What's strange is if I don't define those two fields as ChoiceField-type in my ModelForm class, they render as normal text input fields in HTML and pre-populate using the database values. But when I define them so they show up as select-option input fields in HTML, nothing is pre-selected. Can I change this so that the values from the database are pre-selected?
EDIT: As requested here is the code for my model and form:
class App(models.Model):
CODES = (
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
code = models.CharField(max_length=1, choices=CODES)
start_time = models.TimeField(blank=True, null=True)
end_time = models.TimeField(blank=True, null=True)
class AppForm(ModelForm):
CODES = (
(u'',u'Please select code'),
(u'a',u'annual'),
(u'm',u'monthly'),
(u'w',u'weekly')
)
TIMES = (
(u'00:00',u'All Day'),
(u'12:00',u'Noon')
)
start_time = forms.ChoiceField(required=False, choices=TIMES)
end_time = forms.ChoiceField(required=False, choices=TIMES)
code = forms.ChoiceField(choices=CODES, label='Type')
class Meta:
model = App
Interestingly, code field has the model instance value preselected just fine when rendered as HTML. I wonder if having the choices argument in the model definition makes the difference here?
UPDATE: I just noticed that if I pull up an App instance in the python manage.py shell like so:
a = App.objects.get(id=16)
a.start_time
I get a value like datetime.time(12, 0). But in the Django admin, when I'm looking at all of the App instances, all of them show (None) under start_time and end_time. Why would that be?
In response to your update : your times strings match default time string HH:MM format. Just like a user would enter them from website manually 12:00. The values get parsed and turned into time at model save (at validating really).
And when you load model - then of course the initial values loaded from object match the field's (models.TimeField) type.
If you replace your TIMES with
(datetime.time(0,0),u'All Day'),
(datetime.time(12,0),u'Noon')
your troubles should be over.
Alan