How to use a DatePicker in a ModelForm in django? - python

I am using django 3.0 and I am trying to display a datepicker widget in my ModelForm, but I can't figure out how (all I can get is text field). I have tried looking for some solutions, but couldn't find any. This is how my Model and my ModelForm look like:
class Membership(models.Model):
start_date = models.DateField(default=datetime.today, null=True)
owner = models.ForeignKey(Client, on_delete=models.CASCADE, null=True)
type = models.ForeignKey(MembershipType, on_delete=models.CASCADE, null=True)
class MembershipForm(ModelForm):
class Meta:
model = Membership
fields = ['owner', 'start_date', 'type']
widgets = {
'start_date': forms.DateInput
}
And this is my html:
<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

Although #willem-van-onsem's answer is great, there are a few alternatives that do not require additional dependencies.
A few options, in order of increasing effort:
Use a SelectDateWidget instead of the default DateInput (no JavaScript required):
class MyForm(forms.Form):
date = forms.DateField(widget=forms.SelectDateWidget())
Use the browser's built-in date picker, by implementing a customized widget that uses the HTML <input type="date"> element (no JavaScript required):
class MyDateInput(forms.widgets.DateInput):
input_type = 'date'
class MyForm(forms.Form):
date = forms.DateField(widget=MyDateInput())
or, alternatively:
class MyForm(forms.Form):
date = forms.DateField(widget=forms.DateInput(attrs=dict(type='date')))
Use the date picker from django.contrib.admin, as described here in detail. In short, there are a few things you would need:
from django.contrib.admin.widgets import AdminDateWidget
...
class MyForm(forms.Form):
date = forms.DateField(widget=AdminDateWidget())
then, to make this work, add the following dependencies to your template <head>:
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
<script src="{% static 'admin/js/core.js' %}"></script>
<script src="{% url 'admin:jsi18n' %}"></script> {# see note below #}
{{ form.media }} {# this adds 'calendar.js' and 'DateTimeShortcuts.js' #}
Now there's one catch: the admin:jsi18n url only works for users with admin access, so you may need to replace this and define an alternative path in your urls.py, e.g.:
from django.views import i18n
...
urlpatterns = [
...,
path('jsi18n/', i18n.JavaScriptCatalog.as_view(), name='jsi18n'),
]
Finally, here's what the widgets look like (on firefox):
Personally I like the second option best. It also allows us to specify initial, minimum and maximum values (in django you can do this e.g. using the attrs argument). Here's a quick snippet to show the HTML element in action:
<input type="date" value="2021-09-09" min="2021-09-09">

Django 4.0. Leaving this here incase it helps someone else.
This will set the minimum date and default value to today's date and should be used in forms.py. In my case I use crispy forms in my .html to render the field.
from datetime import date
today = date.today()
class DateForm(forms.ModelForm):
target_Date = forms.DateField(widget=forms.TextInput(attrs={'min': today, 'value': today, 'type': 'date'}), required=True)
class Meta:
model = DateForm
fields = ['target_Date']

This is the expected behavior. A DateInput widget [Django-doc] is just a <input type="text"> element with an optional format parameter.
You can make use of a package, like for example django-bootstrap-datepicker-plus [pypi]
, and then define a form with the DatePickerInput:
from bootstrap_datepicker_plus import DatePickerInput
class MembershipForm(ModelForm):
class Meta:
model = Membership
fields = ['owner', 'start_date', 'type']
widgets = {
'start_date': DatePickerInput
}
In the template you will need to render the media of the form and load the bootstrap css and javascript:
{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{{ form.media }}
<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>

As other have said, this is expected since its just a special text field.
An alternative I prefer is using django-widget-tweaks as this pushes front-end customizations back to your template instead of editing forms.py on the backend. Saving/testing is also faster since the app doesn't have to reload with each save.
Install to your environment:
pip install django-widget-tweaks
Add to installed apps:
INSTALLED_APPS = [
...
"widget_tweaks",
]
Add to your template:
{% extends 'app/base.html' %}
{% load widget_tweaks %}
Use render_field with input tag attributes to customize your field. Eg below using bootstrap 5. Notice how we can specify attributes such as type and class within the template tag:
<div class="col-2">
<label for="{{ form.date.id_for_label }}" class="col-form-label">{{ form.year.label }}</label>
</div>
<div class="col-4">
{% render_field form.year type="date" class="form-control" placeholder="mm/dd/yyyy" %}
<div>

Related

Change layout of django-filter form

I am currently working on my first bigger django project and I am facing issues regarding the layout of my data filters made with django-filters.
The default layout of the django-filter form is a vertical list (see image), but I need to make it horizontal, consisting of two rows (labels/fields).
What is the (best practice) way to edit the layout?
Is there a way I can access every Label/Field-Item of the form from within the template, so I can use Bootstrap5 Grid?
One restriction is, that my template will be used by differents models/filters, so I need to configure the layout dynamically.
Every hint is very much appreciated :)
Thank you!
My template (relevant section)
<form method="get" class="form">
<button type="submit" class ="btn btn-primary">Filtern</button>
{% crispy filter.form %}
</form>
my django-filter filter class
class EquipmentFilter(FilterSet):
class Meta:
model = Equipment
fields = {'name': ['icontains'], 'description': ['icontains']}
my model
class Equipment(models.Model):
"""Device that can execute a measurement"""
name = models.CharField("Name", max_length=50)
description = models.CharField("Description", max_length=100)
configuration = models.JSONField("Configuration", default=dict)
equipment_category = models.ForeignKey("EquipmentCategory", on_delete=models.CASCADE, verbose_name="Equipment Category")
filter.form is a normal Django form, so you can layout it the way you want.
I see that you use crispy forms. But you can style it anyway you want, boostrap-way included.
Here is an example from the official documentation
You can use the .as_p method to get them in <p><label><input></label></p> format or you can generate them by hand:
<form method="get" class="form">
<button type="submit" class ="btn btn-primary">Filtern</button>
{{ filter.form.non_field_errors }}
<div class="boostrap-class">
{{ filter.form.name.errors }}
<label for="{{ filter.form.name.id_for_label }}" class="another-boostrap-class">Name</label>
{{ filter.form.name }}
</div>
<div class="boostrap-class">
{{ filter.form.description.errors }}
<label for="{{ filter.form.description.id_for_label }}" class="another-boostrap-class">Description</label>
{{ filter.form.description }}
</div>
</form>
You can also specify a custom Form class in your filterset meta (EquipmentFilter.Meta) and use techniques presented in this other SO question

Django Bootstrap Date Picker default value / todayBtn problem

I created a Date Picker using Django and Bootstrap. When clicking on the calendar field on the right of the input field, however, the calendar jumps to a default of: 06/Fr/yyyy. I tried setting the todayBtn attribute to True but that produced an error. Example can be seen on the following image:
My models.py:
class TimeInterval(models.Model):
time_interval = models.DateField()
forms.py:
from .models import TimeInterval
from bootstrap_datepicker_plus import DatePickerInput
class TimeIntervalForm(forms.ModelForm):
date = forms.DateField(
widget=DatePickerInput(
options={
"format": "mm/dd/yyyy",
# "todayHighlight": True,
# "todayBtn": True,
}
)
)
class Meta:
model = TimeInterval
fields = '__all__'
views.py:
def datepicker_form_view(request):
return render(request, "datepicker_form.html", {
"time_interval_form": TimeIntervalForm(),
})
And my HTML file:
{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
<form action="." method="POST">{% csrf_token %}
{% bootstrap_form time_interval_form %}
{{time_interval_form.media }}
<input type="submit" value="Submit">
</form>
I came across a similar question about the todayHighlight attribute but there the OP used JavaScript/jQuery, hence it was advised that he added that attribute using a jQuery select. I feel that in my case, expanding the HTML code with a <script> section and a jQuery selection just to have that one attribute working is an overkill - there must be a more convenient way to go about this that I'm not aware of.
Try something like this
date = forms.DateTimeField(error_messages={'invalid': 'Wrong date.'},
required=False, widget=DateInput(attrs={
'class': 'datepicker form-control input-sm ',
'data-trigger': 'focus'}))
or
date = forms.DateTimeField(required=False,
widget=DateInput(attrs={'class': 'form-control input-sm'},
format=('%Y-%m-%d')))

Django: DatePicker with "django-widget-tweaks"

Good day,
I'm trying "create" a DatePicker for one of my Inputfields in Django but it's not working!
In my models.py:
class Customer(models.Model):
...
name = models.CharField()
date = models.DateField()
In my views.py:
def Page(request):
CustomerFormSet = modelformset_factory(Customer, fields='__all__')
formset = CustomerFormSet (queryset=Customer.objects.none())
...
context = {'formset': formset}
return render(request, 'app/base.html', context)
In my template:
{% extends 'app/base.html' %}
{% load widget_tweaks %}
<form actions="" method="POST">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
...
{% render_field form.name class="form-control" %}
...
{% render_field form.date class="form-control" %}
...
Now my first Inputfield works fine! It returns a fitting Field in Bootstraps "Form-Group"-Layout. But my InputField for Dates remains a simple TextInput with no calendar apearing to choose from.
My Question is: am I doing something wrong or is it still impossible to obtain such a function in this way?
Thanks and a nice evening to all of you.
If you ara using ModelForm try:
from django import forms
class DateInput(forms.DateInput):
input_type = 'date'
class DataTreinoForm(forms.ModelForm):
class Meta:
model = models.YOURMODEL
fields = _all_
widgets = {
'dateField': DateInput
}
The default format is mm/dd/yyyy. I don't know how to change it in this way.
I just solved this too. Add type="date" to the render_field.
{% render_field form.date type="date" class="form-control" %}
You can add any input tag attributes here which is convenient because
Modify form appearance on the template instead of forms.py, which is conceptually consistent
When you save templates, it doesnt reload the app, so faster testing with html

How to display django model data on the django cms page template

I would like to be able to use my external app data on django cms page.
I am able to use custom plugin data but not data from normal django app
I tried creating views to handle my data but how do I call this view from django cms pages?
here is exactly what I am asking for but his explanation is shallow and the link provided in the answer is no longer in use.
Here is my model:
class ExternalArticle(models.Model):
url = models.URLField()
source = models.CharField(
max_length=100,
help_text="Please supply the source of the article",
verbose_name="source of the article",
)
title = models.CharField(
max_length=250,
help_text="Please supply the title of the article",
verbose_name="title of the article",
)
class Meta:
ordering = ["-original_publication_date"]
def __str__(self):
return u"%s:%s" % (self.source[0:60], self.title[0:60])
My template has placeholders
{% load cms_tags %}
{% block title %}{% page_attribute "page_title" %}{% endblock title %}
{% block content %}
<section class="section">
<div class="container">
<div class="row">
<!-- header-->
<div class="col-lg-12">
<div class="updates">
{% placeholder "header" %}
</div>
</div>
<!-- header end-->
</div> <!-- end row -->
but I don't mind displaying this data anywhere on the template if not possible inside a place holder
I have a custom page that I am using in Django cms.
I would like to display the above data is a section in the Django cms page
If this model was inheriting from CMSPlugin then that would be easy because I could use a custom plugin in my placeholder
I expect to display the data from my model in the template.
I was able to achieve this by doing the following:
#plugin_pool.register_plugin
class ArticlesPluginPublisher(CMSPluginBase):
model = ArticlesPluginModel
name = _("Articles")
render_template = "article_plugin/articles.html"
cache = False
def render(self, context, instance, placeholder):
context = super(ArticlesPluginPublisher, self).render(
context, instance, placeholder
)
context.update(
{
"articles": Article.objects.order_by(
"-original_publication_date"
)
}
)
return context
The plugin model(ArticlesPluginModel) is just for storing the configurations for the instance of the plugin. Not the actual articles.
Then the render will just add to the context the relevant articles from the external app(Article)
You must somehow connect the ExternalArticle with a page object. For example
by defining the ExternalArticle as a page extension
or with an AppHook
or - low-tech - with a PageField on the ExternalArticle model
{% load cms_tags %}
<h1>{{ instance.poll.question }}</h1>
<form action="{% url polls.views.vote poll.id %}" method="post"> {% csrf_token %}
{% for choice in instance.poll.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>

Django ClearableInputField in form doesn't render in HTML [duplicate]

This question already has an answer here:
Customize the styles of Django ClearableFileInput widget
(1 answer)
Closed 6 years ago.
I'm working on a web app that has photo upload functionality. I created a ModelForm to gather minimal user info plus a photo, and when I render it in HTML as {{ form.as_p }}, the field that allows the user to upload an image shows up just fine. The problem is, the form doesn't look good.
I need to be able to manually render the form in order to make it look better. I have written the HTML for this, and everything looks right except for the ImageFileField. Only the label gets rendered, not the upload button, checkbox to clear the file, etc.
What do I need to do to get the ImageFileField from the ModelForm to render correctly in my custom HTML? I've looked at the Django docs up and down, looked here on SO and can't find anyone else who's had this issue. Many thanks in advance!
forms.py snippet
class PostForm(forms.ModelForm):
class Meta:
model = Items
fields = ('title', 'description', 'image_file')
new_item.html snippet
<form enctype="multipart/form-data" method="post" action="" class="post-form">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="{{ form.title.id_for_label }}">Title:</label><br>
{{ form.title }}
</div><br>
<div class="fieldWrapper">
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">Description: </label><br>
{{ form.description }}
</div><br>
<div class="fieldWrapper">
{{ form.image_field.errors }}
<label for="{{ form.image_field.id_for_label }}">Image (optional):</label><br>
{{ form.image_field }}
</div>
<button type="submit" class="save btn btn-default">Save</button>
</form>
models.py snippet
class Items(models.Model):
title = models.CharField(max_length=1000, null=False)
description = models.TextField(max_length=1000, null=False)
image_file = models.ImageField(max_length=1000,
blank=True,
default='',
null=True,
upload_to='item_photos')
By default django ModelForm uses django.forms.ImageField and not ClearableInputField for django.db.ImageField as revealed at https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#field-types
And I do believe you actually meant ClearableFileInput
ClearableFileInput¶
class ClearableFileInput File upload input: ,
with an additional checkbox input to clear the field’s value, if the
field is not required and has initial data.
How you can make use of it is by changing the widget in the class meta
class PostForm(forms.ModelForm):
class Meta:
model = Items
fields = ('title', 'description', 'image_file')
widgets = {
'name': ClearableFileInput(),
}
I ended up using the Chrome tool to inspect the HTML source for the page that rendered correctly (but ugly), and used that as a guide to custom build the form in HTML to my liking. This is what I needed to add into my HTML form to get it right:
{% if item.image_file %}
Currently:
{{item.image_file.url}}
<input id="image_file-clear_id" name="image_file-clear" type="checkbox" /> <label for="image_file-clear_id">Clear</label><br />Change: <input id="id_image_file" name="image_file" type="file" /></p>
{% endif %}
{% if not item.image_file %}
<input id="id_image_file" name="image_file" type="file" /></p>
{% endif %}

Categories