Add fields to Django ModelForm that aren't in the model

大兔子大兔子 提交于 2019-12-14 03:43:27

问题


I have a model that looks like:

class MySchedule(models.Model):
  start_datetime=models.DateTimeField()
  name=models.CharField('Name',max_length=75)

With it comes its ModelForm:

class MyScheduleForm(forms.ModelForm):
  startdate=forms.DateField()
  starthour=forms.ChoiceField(choices=((6,"6am"),(7,"7am"),(8,"8am"),(9,"9am"),(10,"10am"),(11,"11am"),
      (12,"noon"),(13,"1pm"),(14,"2pm"),(15,"3pm"),(16,"4pm"),(17,"5pm"),
      (18,"6pm"
  startminute=forms.ChoiceField(choices=((0,":00"),(15,":15"),(30,":30"),(45,":45")))),(19,"7pm"),(20,"8pm"),(21,"9pm"),(22,"10pm"),(23,"11pm")))

  class Meta:
    model=MySchedule

  def clean(self):
    starttime=time(int(self.cleaned_data.get('starthour')),int(self.cleaned_data.get('startminute')))
    return self.cleaned_data

  try:
    self.instance.start_datetime=datetime.combine(self.cleaned_data.get("startdate"),starttime)

  except TypeError:
    raise forms.ValidationError("There's a problem with your start or end date")

Basically, I'm trying to break the DateTime field in the model into 3 more easily usable form fields -- a date picker, an hour dropdown, and a minute dropdown. Then, once I've gotten the three inputs, I reassemble them into a DateTime and save it to the model.

A few questions:

1) Is this totally the wrong way to go about doing it? I don't want to create fields in the model for hours, minutes, etc, since that's all basically just intermediary data, so I'd like a way to break the DateTime field into sub-fields.

2) The difficulty I'm running into is when the startdate field is blank -- it seems like it never gets checked for non-blankness, and just ends up throwing up a TypeError later when the program expects a date and gets None. Where does Django check for blank inputs, and raise the error that eventually goes back to the form? Is this my responsibility? If so, how do I do it, since it doesn't evaluate clean_startdate() since startdate isn't in the model.

3) Is there some better way to do this with inheritance? Perhaps inherit the MyScheduleForm in BetterScheduleForm and add the fields there? How would I do this? (I've been playing around with it for over an hours and can't seem to get it)

Thanks!

[Edit:] Left off the return self.cleaned_data -- lost it in the copy/paste originally


回答1:


  1. If I were you, I would have used the customised Django-admin date/time widget(s) for entering date/time entries.

  2. Regarding form validation, make sure you pass the form associated with the request for it to show up form-based errors. (Sample code below)

  3. As for using inheritance, it would be a overkill for this use-case as it will not serve any purpose and it would be better to keep things simple.

Sample code:

if request.POST:
    form = MyScheduleForm(request.POST)
    if form.is_valid():
        # Specific stuff with the variables here
        pass
else:
    form = MyScheduleForm()



回答2:


Ok, I think I figured it out:

As of Django 1.2, running is_valid() triggers MODEL validation on ModelForms. I had assumed that fields would be checked for blank values BEFORE hitting the model clean() function, so my clean function doesn't check for blank values or None types. Basically, my clean() in my model looks something like:

def clean(self):
  if self.start_datetime >  datetime.now():
        raise ValidationError('Start date can\'t be in the future')

So I suppose that mostly answer my question. However, I have 1 remaining question:

Is it best to check for blank values in the model clean(), or is there a better way to do this? Seems hackish to check for blanks in the model instead of in the ModelForm -- is the validation on the form field supposed to flag missing inputs on required fields?

Thanks for everyone's help.




回答3:


1: I don't think it's wrong, because you have some very specific stuff going on there:

  • Specific time entries (noon, ending at 5PM..)
  • 15 minute increments for startminutes

2: Update: comment below says your field should be required=True by default. It's true, you should be getting a ValidationError with your form if the field is left blank.

Can you post the TypeError you're speaking about? Is it happening outside the clean() block? Because if you don't return cleaned_data from your clean function like in your example, your form won't have any data to work with even if it initially checks out by not raising any ValidationErrors.

Anyways, you can explore the clean_ methods for per field validation.

def clean_startdate(self):  
    if not self.cleaned_data['startdate']:
            raise forms.ValidationError("Must enter a start date")

http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#overriding-the-clean-method

3: Can you clarify here what you're trying to do with inheritance? It looks like your field definitions are very specific to this form, so it belongs right here in the MyScheduleForm. Inheritance is for re-using code : )

If you are looking to reuse this for multiple DateTimeFields, yes you can use form inheritance. You could define a ModelForm like you have now, subclass it, and override the parent's Meta as shown here in the docs to use it on multiple models: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#form-inheritance

I'd also check out how the django does its SplitDateTimeWidget (check the source): http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.SplitDateTimeWidget

There are some other '3rd party' split date time widgets worth taking a look at on the interwebs too!




回答4:


For form fields that may contain blank values, you must declare the field as follows:

start_datetime=models.DateTimeField(blank=True, null=True)

This tells the form that it can be blank, and that the database field can be null. That may fix that problem.

Why are you using a ModelForm if you're trying to include fields that are not a part of the Model? ModelForms are designed to quickly create forms that bind directly to your model. Of course they have various customizations, but changing the actual fields seems to me something that a regular Form is for.

Otherwise, if you just want to split the VIEW of the formm, not the form itself, create a custom widget to display the DateTime field, such as the SplitDateTimeWidget. Subclass it, and provide your CHOICES for the values of the drop down.



来源:https://stackoverflow.com/questions/4665719/add-fields-to-django-modelform-that-arent-in-the-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!