Добавить поля в Django ModelForm, которые не входят в модель

У меня есть модель, которая выглядит так:

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

С его помощью появляется 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 a problem with your start or end date")

В принципе, я пытаюсь сломать поле DateTime в модели на 3 более удобных для использования поля формы - выбор даты, выпадающее меню и минутное раскрывающееся меню. Затем, как только я получил три входа, я снова их собираю в DateTime и сохраняю в модели.

Несколько вопросов:

1) Не совсем ли это так? Я не хочу создавать поля в модели в течение нескольких часов, минут и т.д., Так как все это в основном только промежуточные данные, поэтому я хотел бы разбить поле DateTime на подполя.

2) Трудность, с которой я сталкиваюсь, заключается в том, что поле startdate пустое - кажется, что он никогда не проверяется на отсутствие пробелов и просто заканчивается тем, что он запускает TypeError позже, когда программа ожидает дату и получает Никто. Где Django проверяет пустые входы и поднимает ошибку, которая в конечном итоге возвращается к форме? Это моя ответственность? Если да, то как это сделать, поскольку он не оценивает clean_startdate(), поскольку startdate не находится в модели.

3) Есть ли лучший способ сделать это с наследованием? Возможно, наследуйте MyScheduleForm в BetterScheduleForm и добавьте туда поля? Как мне это сделать? (Я играю с ним более часа и, похоже, не могу получить его)

Спасибо!

[Edit:] Отключен возврат self.cleaned_data - потерял его в оригинале copy/paste

Ответ 1

  • Если бы я был вами, я бы использовал настраиваемые виджеты даты/времени Django-admin для ввода записей даты/времени.

  • Что касается проверки формы, убедитесь, что вы передали форму, связанную с запросом, чтобы она отображала ошибки на основе форм. (Пример кода ниже)

  • Что касается использования наследования, это будет излишним для этого случая использования, поскольку оно не будет служить какой-либо цели, и было бы лучше, если бы все было просто.

Пример кода:

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

Ответ 2

Хорошо, я думаю, что понял:

Как и в Django 1.2, running is_valid() запускает проверку модели на ModelForms. Я предположил, что поля будут проверяться на пустые значения перед ударом функции clean(), поэтому моя чистая функция не проверяет пустые значения или типы None. В принципе, мой clean() в моей модели выглядит примерно так:

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

Поэтому я полагаю, что в основном отвечаю на мой вопрос. Однако у меня остается 1 вопрос:

Лучше ли проверять пустые значения в модели clean(), или есть лучший способ сделать это? Кажется, что взломанный для проверки пробелов в модели, а не в ModelForm, - это проверка на поле формы, которое должно помечать отсутствующие входы в обязательных полях?

Спасибо за помощь всем.

Ответ 3

1: Я не думаю, что это неправильно, потому что у вас есть очень специфический материал:

  • Конкретные записи времени (полдень, заканчивающиеся на 5 вечера).
  • 15-минутные приращения для startminutes

2: Обновление: комментарий ниже говорит, что ваше поле должно быть required=True по умолчанию. Это правда, вы должны получать ValidationError с вашей формой, если поле остается пустым.

Можете ли вы опубликовать TypeError, о котором вы говорите? Это происходит за пределами блока clean()? Поскольку, если вы не возвращаете cleaned_data из своей чистой функции, как в вашем примере, ваша форма не будет содержать никаких данных, даже если она изначально проверяет, не поднимая никаких ValidationErrors.

В любом случае вы можете изучить методы clean_ для каждой проверки поля.

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: Можете ли вы пояснить, что вы пытаетесь сделать с наследованием? Похоже, что ваши определения полей очень специфичны для этой формы, поэтому она принадлежит прямо здесь, в MyScheduleForm. Наследование предназначено для повторного использования кода:)

Если вы хотите повторно использовать это для нескольких DateTimeField s, да, вы можете использовать наследование формы. Вы можете определить ModelForm, как сейчас, подклассировать его и переопределить родительский Meta, как показано здесь в документах, чтобы использовать его на нескольких моделях: http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#form-inheritance

Я также проверил, как django делает свой SplitDateTimeWidget (проверьте источник): http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.SplitDateTimeWidget

Есть также некоторые другие "сторонние" таймеры с датой подсчета, которые стоит посмотреть и на interwebs!

Ответ 4

Для полей формы, которые могут содержать пустые значения, вы должны объявить это поле следующим образом:

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

Это сообщает форме, что это может быть blank, и что поле базы данных может быть null. Это может решить эту проблему.

Почему вы используете ModelForm, если вы пытаетесь включить поля, которые не являются частью модели? ModelForms предназначены для быстрого создания форм, которые напрямую привязаны к вашей модели. Конечно, у них есть различные настройки, но изменение фактических полей мне кажется чем-то, для чего нужна обычная форма.

В противном случае, если вы просто хотите разделить VIEW формы, а не форму, создайте собственный виджет, чтобы отобразить поле DateTime, например SplitDateTimeWidget. Подклассируйте его и предоставьте свои CHOICES для значений выпадающего списка.