How to add datepicker and timepicker to Django forms.SplitDateTimeField? - django

I have a form that splits the date and time from a datetime field in the model.
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('name', 'description', 'start', 'end',)
widgets = {
'start': forms.SplitDateTimeWidget(),
'end': forms.SplitDateTimeWidget(),
}
How can I add a datepicker and timepicker to each separate input box that is rendered?
Setting:
'start': forms.SplitDateTimeWidget(attrs={'type': 'date'})
makes both inputs datepicker but I need the second one to be a timepicker..
I am using Django 2.0, bootstrap and crispy forms

forms.SplitDateTimeWidget() renders an Html input that may contain some attributes that you will need in your template:
forms.SplitDateTimeWidget(attrs={'attrs': 'attrs'})`.
# You don't need to edit the "input:type", type text is good
The rendering will be something like that
<input type='text' name='field_name_0' id='id_field_name_0' attrs='attrs'>
<input type='text' name='field_name_1' id='id_field_name_1' attrs='attrs'>
According to the documentation, New in Django 2.*
You can add seperate attributes.
# Free to add attributes that you want
'start': forms.SplitDateTimeWidget(
date_attrs({'class':'datepicker'}), # or override the ID, "id":id
time_attrs({'class':'timepicker'}),
)
Since I do not know what kind of Datetime neither Timepicker that you use in your project. So, in your js call each of them by their conventional name class...
$(".datepicker").datepicker();
$(".timepicker").timepicker();

Related

How to make a date picker that does not select previous dates in Django

I want to make a date picker that does not select previous dates using Django.
class DateInput(forms.DateInput):
input_type = 'date'
class TimeInput(forms.TimeInput):
input_type = 'time'
"""class used for booking a time slot."""
class BookingForm(forms.ModelForm):
class Meta:
model = Booking
fields = ['check_in_date', 'check_in_time', 'check_out_time',
'person', 'no_of_rooms']
widgets = {
'check_in_date': DateInput(),
'check_in_time': TimeInput(),
'check_out_time': TimeInput(),
}
"""Function to ensure that booking is done for future and check out is after check in"""
def clean(self):
cleaned_data = super().clean()
normal_book_date = cleaned_data.get("check_in_date")
normal_check_in = cleaned_data.get("check_in_time")
normal_check_out_time = cleaned_data.get("check_out_time")
str_check_in = str(normal_check_in)
format = '%H:%M:%S'
try:
datetime.datetime.strptime(str_check_in, format).time()
except Exception:
raise ValidationError(
_('Wrong time entered.'),
code='Wrong time entered.',
)
# now is the date and time on which the user is booking.
now = timezone.now()
if (normal_book_date < now.date() or
(normal_book_date == now.date() and
normal_check_in < now.time())):
raise ValidationError(
"You can only book for future.", code='only book for future'
)
if normal_check_out_time <= normal_check_in:
raise ValidationError(
"Check out should be after check in.", code='check out after check in'
)
The above code is written in forms.py file. As you can see, I made a date picker but the problem is that the user can select any date but I want him to select only current or future dates. Perhaps, it can be done using JavaScript or bootstrap but I want to know can we do it in Django?
You can specify a min attributeĀ [mdn-doc] for the date input type:
from django.utils.timezone import now
class FutureDateInput(forms.DateInput):
input_type = 'date'
def get_context(self, name, value, attrs):
attrs.setdefault('min', now().strftime('%Y-%m-%d'))
return super().get_context(name, value, attrs)
You will need to do validation in the form/model, since this will only limit this at the client side.
Django is a web framework that allows you to create dynamic web applications using Python. One of the features that Django provides is the ability to create forms that can validate user input and display errors. You can also use widgets to customize the appearance and behavior of the form fields.
One of the widgets that Django offers is the DateInput widget, which renders a text input field that can be attached to a date picker using JavaScript. However, by default, the date picker does not restrict the user from selecting previous dates, which might not be desirable for some applications.
To make a date picker that does not select previous dates using Django, you need to do the following steps:
Step 1: Install a JavaScript library that provides a date picker
There are many JavaScript libraries that can provide a date picker functionality, such as jQuery UI, Bootstrap Datepicker, or Flatpickr. You can choose any of them according to your preference and compatibility with your project. For this example, we will use Flatpickr, which is a lightweight and customizable date picker library.
To install Flatpickr, you can use a package manager like npm or yarn, or you can download the files from the official website and include them in your project. You will need to include the flatpickr.min.js and flatpickr.min.css files in your HTML template.
Step 2: Create a custom widget that inherits from DateInput and adds the Flatpickr attributes
To use Flatpickr with Django, you need to create a custom widget that inherits from the DateInput widget and adds the necessary attributes to the input field. The attributes are:
data-toggle: This tells Flatpickr to attach itself to the input field.
data-min-date: This tells Flatpickr the minimum date that can be selected. You can use a fixed date or a dynamic date based on the current date. For example, "today" means the user can only select today or future dates.
data-date-format: This tells Flatpickr the format of the date that will be displayed and submitted. You can use any format that is supported by Flatpickr, but make sure it matches the format that Django expects. For example, "%Y-%m-%d" means the date will be in the format of year-month-day.
The custom widget can be defined as follows:
from django.forms import DateInput
class FlatpickrDateInput(DateInput):
def __init__(self, *args, **kwargs):
# You can pass any additional options to Flatpickr as keyword arguments
self.options = kwargs.pop('options', {})
super().__init__(*args, **kwargs)
def build_attrs(self, base_attrs, extra_attrs=None):
# Add the Flatpickr attributes to the input field
attrs = super().build_attrs(base_attrs, extra_attrs)
attrs['data-toggle'] = 'flatpickr'
attrs['data-min-date'] = self.options.get('min_date', 'today')
attrs['data-date-format'] = self.options.get('date_format', '%Y-%m-%d')
return attrs
Step 3: Use the custom widget in your form field
To use the custom widget in your form field, you need to pass it as the widget argument when defining the field. For example, if you have a model that has a date field, you can create a model form that uses the custom widget as follows:
from django import forms
from .models import MyModel
from .widgets import FlatpickrDateInput
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['date']
widgets = {
'date': FlatpickrDateInput(options={'date_format': '%d/%m/%Y'})
}
Step 4: Render the form in your template and initialize Flatpickr
To render the form in your template, you can use the standard Django template tags, such as {{ form }} or {{ form.date }}. You also need to include the Flatpickr files and initialize the library by calling the flatpickr function on the input fields that have the data-toggle attribute. For example, your template could look like this:
<html>
<head>
<!-- Include the Flatpickr files -->
<link rel="stylesheet" href="flatpickr.min.css">
<script src="flatpickr.min.js"></script>
</head>
<body>
<!-- Render the form -->
<form method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
<!-- Initialize Flatpickr -->
<script>
// Select all the input fields that have the data-toggle attribute
var dateInputs = document.querySelectorAll('[data-toggle="flatpickr"]');
// Call the flatpickr function on each input field
dateInputs.forEach(function(input) {
flatpickr(input);
});
</script>
</body>
</html>
Conclusion
By following these steps, you can make a date picker that does not select previous dates using Django and Flatpickr. This can improve the user experience and the data validation of your web application. You can also customize the date picker further by using other options and methods that Flatpickr provides. You can find more information and examples on the official documentation of Flatpickr: https://flatpickr.js.org/
Simply you can add this validation method inside model.
import datetime
from django.core.exceptions import ValidationError
class modelname(models.Model):
def Date_validation(value):
if value < datetime.date.today():
raise ValidationError("The date cannot be in the past")
date = models.DateField(default=datetime.date.today, validators=[Date_validation])
It means you cannot select past date.

How to specify camera capture in input tag using Django?

I'm currently developing an application using Django.
I want to open up camera capture directly when specified as follows in HTML5 tag using Django.
<input type="file" accept="image/*" capture="camera"/>
In this case, how can I make a field in a model?
Thanks in advance.
Django 3.2.9
Input attributes are set in the widget of the form field, not in the model. In your model you can simply use a FileField.
For example, using a plain form in your forms.py:
class Myform(forms.Form):
myfield=forms.FileField(
widget=forms.FileInput(attrs={'accept': 'image/*', 'capture':'camera'})
)
In case of a model form:
class Myform(forms.ModelForm):
class Meta:
model = YourModel
fields = [myfile]
widgets = {
'myfile': forms.FileInput(attrs={'accept': 'image/*', 'capture':'camera'})
}
You need a FileField type field, your model should have field like this
img = models.FileField(upload_to="/your-local-path")

Django DetailView - display boolean from models as checkboxes

There's a number of snippets to display booleans in Django Forms as checkboxes (i.e. specifying Checkbox as a widget). For instance (assuming a boolean field defined in the model for bar):
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = ['bar']
widgets = {
'bar' : CheckboxInput(attrs={'class': 'required checkbox form-control'}),
}
However I also need to display a (disabled) checkbox in DetailView (client says so). But I can't figure out an elegant way to do this, since I don't have a form meta for details view...
My current thinking is something like this (bootstrap checkbox):
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" {% if foo.bar %}checked{% endif %} disabled>Bar
</label>
<\div>
Any way to accomplish this in a fashion closer to the Form's widgets?
in the view get you form and set initial value
get the model object and set bars initial value
form = YourForm(initial={'bar':modelObject.bar })
and then send the form to the template and simply render
like form.bar
you can disable this with many ways
like
class FooForm(forms.ModelForm):
class Meta:
model = Foo
fields = ['bar']
widgets = {
'bar' : CheckboxInput(attrs={'class': 'required checkbox form-control','disabled':'disabled or true'}),
}
or find and use any template filter to add attribute to form field

How to add custom css class to django-filters field

I tried to make a way that worked with forms, but here the class did not apply
class TrainerFilter(django_filters.FilterSet):
price_lt = django_filters.NumberFilter(field_name="prise", lookup_expr='lt')
class Meta:
model = Profile
fields = ['direction', 'group', 'child', 'price_lt']
widgets = {
'direction': SelectMultiple(attrs={'class': 'custom-select'}),
}
In case somebody stumbles upon this, one solution might be to add the class to the field's widget attrs, like so:
class TrainerFilter(django_filters.FilterSet):
...
def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
super(TrainerFilter, self).__init__(data=data, queryset=queryset, request=request, prefix=prefix)
self.filters['direction'].field.widget.attrs.update({'class': 'custom-select'})
You were pretty close. This should work:
class TrainerFilter(django_filters.FilterSet):
price_lt = django_filters.NumberFilter(field_name="prise", lookup_expr='lt')
direction = django_filters.CharFilter(widgets = SelectMultiple(attrs={'class': 'custom-select'}))
class Meta:
model = Profile
fields = ['group', 'child', 'price_lt']
class UsersFilter(django_filters.FilterSet):
class Meta:
model = UsersModels
fields = ['username', 'email', 'first_name', 'phone']
filter_overrides = {
models.CharField: {
'filter_class': django_filters.CharFilter,
'extra': lambda f: {
'lookup_expr': 'icontains',
'widget': forms.TextInput(attrs={'class': 'form-control'})
},
},
models.EmailField: {
'filter_class': django_filters.CharFilter,
'extra': lambda f: {
'lookup_expr': 'icontains',
'widget': forms.EmailInput(attrs={'class': 'form-control'})
},
},
}
like this
The easiest way I found is using django widget tweaks. And this module can be used with different kinds of forms, not just with filters. After installations add to template
{% load widget_tweaks %}
And now you can add class for your forms like this
{{ filter.form.direction.label_tag }}
{% render_field filter.form.direction class="custom-select" %}
insread
{{ filter.form.direction.label_tag }}
{{ filter.form.direction }}
IMPORTANT: read the bottom paragraph as I discovered an issue doing it this way and have linked to a source that explains how to use this in tandem with widget tweaks to add custom css to the filters.
From the official documentation.
Simply put, add a field into filters meta class named "form" that uses a forms.py class as its value, like so. This effectively extends your forms.py class.
Customize Field Types:
From there you can do customize the fields types like you normally for forms.py like so.
Actually styling the page using CSS
From here you got three options. My least favorite (leads to alot more code). Use widget tweaks to style it on the page. Use boot strap (or really any css files class). Or the most effective way use both boot strap and widget_tweaks while creating templates for your forms.
However, unless your doing really complicated filtering or querying stuff for 90% of use cases you could do something like the following in your forms.py class to make it pretty easy.
Note: I have since found this method unreliable read the bottom paragraph for a solution
IMPORTANT: Django filters unreliably applies the CSS to these fields extending the forms class but it does allow you to still customize the help text, the widgets used, and etc. so I'm leaving it in the answer. It worked for me initially but after expanding the filter to do more complicated things I realized some fields where getting arbitrarily left out. Below is an article that details how to use Django-filters WITH a custom widget tweaks file that does this for you.
Django-filters how to and widget tweaks with css styling
I found a pretty simple solution:
Open your dev tools and look at the element generated by django-filters
django-filters have assigned the element an id like 'id_field'
so just use that id in your CSS to style that element.

How can i set the size of rows , columns in textField in Django Models

I have this Model
summary = models.TextField()
But I want to have only 4 rows and 15 columns.
Also if I do that do I need to install database again or not.
TextField is a type of field, which when used by a ModelForm by default uses the Textarea widget. Fields deal with backend storage, widgets with front-end editing.
So you need to specify the widget you want to go with your field. You want to create a ModelForm and specify the widget you want to use, per the documentation:
from django import forms
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
widgets = {
'summary': forms.Textarea(attrs={'rows':4, 'cols':15}),
}
Here is the form specific equivalent for changing the rows/cols attribute to a textarea. Use the forms.Textarea widget and set attrs dictionary values:
class LocationForm(forms.ModelForm):
auto_email_list = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 2, 'cols': 40}))
notes = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 4, 'cols': 40}))
class Meta:
model = Location
exclude = ['id']
Hope this helps those looking for form specific
If you're using django-crispy-forms, in your __init__() function you can access the widget attrs like so:
self.fields['summary'].widget.attrs['rows'] = 4
self.fields['summary'].widget.attrs['columns'] = 15
I suppose you could set the attrs this way it in a non crispy-form ModelForm, but you may as well do it in your widget definition, as shown above. I just found this post and thought I'd share how I did it with crispyforms.
You can also use jQuery as follows:
$(document).ready(function(){
$("textarea").attr({"rows": "4",
"cols": "15"
});
});
First you have to install django widget tweaks by using:
pip install django-widget-tweaks
Then you have to go to setting.py and add 'widget_tweaks' to installed apps like this:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'widget_tweaks',
)
Then go to your html template and replace this with {{ field }}, to resize your text field:
{% load widget_tweaks %}
{{ mytextareafield|attr:"rows:4" | "cols:30" }}
The django-widget-tweaks package makes this quite simple. In your template:
{% load widget_tweaks %}
...
{{ mytextareafield|attr:"rows:3" }}
Use Widget in Meta.
Keep in Mind that you must add that field in modelform also
class MyModelForm(forms.ModelForm):
address = forms.CharField()
class Meta:
model = MyModel
exclude = ()
widgets = {
address': forms.Textarea(attrs={'rows':1, 'cols':15}),
}
The first answer may be the right one for the question, but the django-widget-tweaks package is also a great option for changing more attributes, for instance set specific CSS class.
So consider that option if you want to alter another attributes easily.