I have created a form in forms.py which uses select input based on objects from the database:
from controls.models import Valve
class CronjobForm(forms.Form):
query = Valve.objects.all()
VALVE_CHOICES = [
(valve.pk, valve.name) for valve in query
]
valves = forms.ChoiceField(required=True, widget=forms.Select, choices=VALVE_CHOICES)
At first, everything seemed to work just fine, the HTML's <select> with option got rendered.
The problem is, when I add a new Valve object to the database, using the Django Admin interface, it takes a while for the ChoiceField field to get updated - I can't see the new option on front-end immediately. I tried to reload the page, force reload, even print out the query to the terminal - the variable query got updated immediately, just the HTML did not.
It eventually gets updated after a while, minute or so, maybe thanks to the system checks or by repeating the runserver command.
So I'm wondering, what am I missing? Is there some way to force update the form? Does the form get cached and that's what causes the problem?
This is because the query is being evaluated when the class is defined, not when the form is instantiated.
Don't do this. Use a ModelChoiceField, which accepts a queryset.
class CronjobForm(forms.Form):
valves = forms.ModelChoiceField(required=True, widget=forms.Select, queryset=Valve.objects.all())
We can also pass a reference of the function which will return the array for choices, as mentioned in the choices' description of django docs.
I also faced the same issue, which was resolved like this.
def get_valve_choices():
return [
(valve.pk, valve.name) for valve in Valve.objects.all()
]
class CronjobForm(forms.Form):
valves = forms.ChoiceField(required=True, widget=forms.Select, choices=get_valve_choices)
Related
I have a Django form that includes a DateInput like this:
Fecha = forms.DateField(widget=DateInput(format = '%Y-%m-%d'),initial=(date.today() - timedelta(1)))
The idea is that every time you access the template that contains the form, it automatically shows the day-1 date.In my development environment this works as expected.However, I've noticed something strange in my production environment. The DateField initial value doesn't change unless I restart my Apache Server. For example, if I restart Apache right now my form will show 2015-02-26 as the initial date and will stay like that until I restart Apache again. I'm not sure if this is Django related or it has more to do with Apache configuration. Any suggestions or workarounds? Thanks in advance!!!!
Regards,
Alejandro
Shouldn't it be like this:
def get_yesterday():
return datetime.today() - timedelta(days=1)
mydatefield = forms.DateField(widget=DateInput(format = '%Y-%m-%d'),initial=get_yesterday)
Your code would evaluate the date.today() function at server initialization and not at form initialization.
This is neither a django issue nor an Apache configuration problem, but rather a result of how and when Python evaluates code. Class attributes are evaluated as soon as the module containing a class is imported/brought into memory. So your form class's Fecha attribute is evaluated only initially when your app starts, and not evaluated again until your app is restarted.
To handle this, simply provide initial date values when your view instantiates your form object instead of in your form class definition. This will work because your view function executes every time you refresh the page:
import MyForm
def my_view(request):
my_form_instance = MyForm(initial={'Fecha': date.today() - timedelta(1)})
...
I am having a problem with my Django code inserting new rows instead of updating existing ones. Many people on here have asked for help with a similar problem, but generally they forget about setting instance as in form = SomeModelForm(request.POST or None, instance=some_model_instance). I seem to be doing that right, but it still doesn't save over existing data.
One other poster had the exact same issue (see Django form INSERTs when I want it to UPDATE). It looks like the code given to him to fix the problem resolved the issue, but no one explained why it worked when he asked!
VIEW
if form_type == "edit_event":
# we need to load all the data based on the event_id
event_id = request.POST['event_id'] or None
event_to_edit = mapDataManager.getEvent(event_id)
if event_to_edit and mapDataManager.userCreatedEvent(request.user, event_to_edit):
# this user actually created this event
# let them edit it
event_form = EventModelForm(request.POST or None, instance=event_to_edit)
location_form = LocationModelForm(request.POST or None, instance=event_to_edit.location)
list_of_event_scheds = EventSchedule.objects.filter(pk=event_id).values()
event_sched_formset = EventScheduleModelFormSet(request.POST or None, initial=list_of_event_scheds)
if event_form and location_form and event_sched_formset:
if event_form.is_valid() and location_form.is_valid() and event_sched_formset.is_valid():
# update event
mapDataManager.updateEvent(event_form, location_form, event_sched_formset)
# redirect
return HttpResponseRedirect(reverse('map'))
else:
# set feedback to show edit event form
feedback = "edit_event"
updateEvent()
def updateEvent(self, event_form, location_form, event_sched_forms):
event = event_form.save(commit=False)
location = location_form.save()
event.location = location
event.save()
for event_sched_form in event_sched_forms: # loop through formset
event_sched = event_sched_form.save(commit=False)
event_sched.event = event
event_sched.save()
Can anyone help me out? Am I missing something really basic?
EDIT: Just to clarify, NONE of my forms were updating, all were inserting. Based on Daniel's suggestion I think I need to use formsets to link all three modelforms that are being edited.
For some strange reason, you're correctly passing instance to the main forms, but initial to EventScheduleModelFormSet, which is the immediate cause of the problem.
However, since these models obviously have a foreign key relationship, it sounds like you should be using a inline model formset:
from django.forms.models import inlineformset_factory
eventschedule_formset_class = inlineformset_factory(Event, EventSchedule)
event_sched_formset = eventschedule_formset_class(request.POST or None,
instance=event_to_edit)
Now there is no need for that updateEvent logic - you can just save event_sched_formset and it will correctly reference the event.
you are trying to save the EventScheduleModelFormSet form? there is no instance set only initial data so you are creating a new object every time. or am I missing something?
I was able to get the inline model formset working as per Daniel's suggestion with help from this blog post: http://charlesleifer.com/blog/djangos-inlineformsetfactory-and-you/
Just as an FYI, one crucial point I was missing is that the formset's instance refers to the parent model. Also, if you're displaying the formset manually in your template, don't forget to include {{ formset.id }}.
I'd like to create a confirmation page for selected objects before a change is made to them (outside the admin). The objects can be of different models (but only one model a time).
This is much like what is done in administration before deletion. But the admin code is complex and I haven't grasped how it is done there.
First I have severall forms that filter the objects differently and then I pass the queryset to the action / confirmation page. I have created a form factory so that I can define different querysets depending on model (as seen in another similiar question here at Stackoverflow):
def action_factory(queryset):
''' Form factory that returns a form that allows user to change status on commissions (sale, lead or click)
'''
class _ActionForm(forms.Form):
items = forms.ModelMultipleChoiceField(queryset = queryset, widget=forms.HiddenInput())
actions = forms.ChoiceField(choices=(('A', 'Approve'), ('D' ,'Deny'), ('W' ,'Under review'), ('C' ,'Closed')))
return _ActionForm
Which I use in my view:
context['form']=action_factory(queryset)()
The problem is that the items field wont be displayed at all in the html-code when it is hidden. When I remove the HiddenInput widget it displays the form correctly.
I don't want to display the choice field since there can be thousands of objects. All I want to have is something like "Do you want to change the status of 1000 objects" and a popdown and a submit button. A simple enough problem it seems, but I can't get it to work.
If someone has a solution to my current attempt I would be glad to hear how they have done it. Even better would be if there is a cleaner and better solution.
I used the wrong widget. It should be MultipleHiddenInput not HiddenInput.
Greetings,
I am trying to implement a TimeField model which only consists of HH:MM (ie 16:46) format, I know it is possible to format a regular Python time object but I am lost about how to manage this with Django.
Cheers
Django widget can be used to achieve this easily.
from django import forms
class timeSlotForm(forms.Form):
from_time = forms.TimeField(widget=forms.TimeInput(format='%H:%M'))
DateTime fields will always store also seconds; however, you can easily tell the template to just show the hours and minute, with the time filter:
{{ value|time:"H:M" }}
where "value" is the variable containing the datetime field.
Of course, you can also resort to other tricks, like cutting out the seconds from the field while saving; it would require just a small change to the code in the view handling the form, to do something like this:
if form.is_valid():
instance = form.save(commit=False)
instance.nosecs = instance.nosecs.strptime(instance.nosecs.strftime("%H:%M"), "%H:%M")
instance.save()
(note: this is an ugly and untested code, just to give the idea!)
Finally, you should note that the admin will still display the seconds in the field.
It should not be a big concern, though, because admin should be only used by a kind of users that can be instructed not to use that part of the field.
In case you want to patch also the admin, you can still assign your own widget to the form, and thus having the admin using it. Of course, this would mean a significant additional effort.
So I think the proposed and accepted solution is not optimal because with:
datetime.widget = forms.SplitDateTimeWidget(time_format=('%H:%M'))
For a SplitDateTimeField in my case but for you only change it to TimeWidget.
Hope it helps other people too.
TimeField model
in Template
Is displayed
{{ value|time:"H:i" }}
Is not displayed
{{ value|time:"H:M" }}
Django 1.4.1
For a ModelForm, you can easily add a widget like this, to avoid the seconds being shown (just show hh:mm):
class MyCreateForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('time_in', 'time_out', )
widgets = {
'time_in': forms.TimeInput(format='%H:%M'),
'time_out': forms.TimeInput(format='%H:%M'),
}
You can at least modify the output in the __str__ method on the model by using datetime.time.isoformat(timespec='minutes'), like this:
def __str__(self):
return self.value.isoformat(timespec='minutes')
Now the value is showing as HH:MM in admin pages.
On Django 1.9 the following format should work:
{{ yourData.value|time:"H:i" }}
Django has a whole set of template tags and filters.
Django 1.9 documentation on this is:
https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#time
This is a follow-up on How do you change the default widget for all Django date fields in a ModelForm?.
Suppose you have a very large number of models (e.g. A-ZZZ) that is growing with the input of other developers that are beyond your control, and you want to change the way all date fields are entered (i.e. by using jQueryUI). What's the best way to ensure that all date fields are filled out using that new widget?
One suggestion from the cited question was:
def make_custom_datefield(f):
if isinstance(f, models.DateField):
# return form field with your custom widget here...
else:
return f.formfield()
class SomeForm(forms.ModelForm):
formfield_callback = make_custom_datefield
class Meta:
# normal modelform stuff here...
However, is this possible to do where you don't have explicit ModelForm's, but url patterns come from models directly? i.e. your url config is likeso:
url(r'^A/?$', 'list_detail.object_list', SomeModelA)
where SomeModelA is a model (not a form) that's turned into a ModelForm by Django in the background.
At present in my system there are no Forms for each Model. The only point of creating forms explicitly would be to add the formfield_callback suggested in the prior solution, but that goes against DRY principles, and would be error prone and labour intensive.
I've considered (as suggested in the last thread) creating my own field that has a special widget and using that instead of the builtin. It's not so labour intensive, but it could be subject to errors (nothing a good grep couldn't fix, though).
Suggestions and thoughts are appreciated.
It sounds like you want to do this project-wide (ie: you're not trying to do this in some cases, but in ALL cases in your running application).
One possibility is to replace the widget attribute of the DateField class itself. You would need to do this in some central location... something that is guaranteed to be loaded by every running instance of the django app. Middleware can help with this. Otherwise, just put it in the __init__ file of your app.
What you want to do is re-assign the widget property for the forms.DateField class itself. When a new DateField is created, Django checks to see if the code specifies any particular widget in the field property definition. If not, it uses the default for DateField. I'm assuming that if a user in your scenario really defined a particular widget, you'd want to honour that despite the change to your global API.
Try this as an example of forcing the default to some other widget... in this case a HiddenInput:
from django import forms
forms.DateField.widget = forms.HiddenInput
class Foo(forms.Form):
a = forms.DateField()
f = Foo()
print f.fields['a'].widget
# results in <django.forms.widgets.HiddenInput object at 0x16bd910>