Подтвердить что ты не робот

Django - обновить модель с помощью FormView и ModelForm

Я не могу понять, как использовать ModelForm в FormView, чтобы он обновлял уже существующий экземпляр

Форма POST на этом URL: r'/object/(?P<pk>)/'

Я использую ModelForm (а не напрямую UpdateView), потому что требуется одно из полей, и я выполняю очистку.

В основном я хотел бы предоставить kwarg instance=... при инициализации формы в FormView (при POST), чтобы она привязалась к объекту, pk которого указана в URL-адресе. Но я не могу понять, где это сделать...

class SaveForm(ModelForm):
    somedata = forms.CharField(required=False)
    class Meta:
        model = SomeModel  # with attr somedata
        fields = ('somedata', 'someotherdata')
    def clean_somedata(self):
        return sometransformation(self.cleaned_data['somedata'])

class SaveView(FormView):
    form_class = SaveForm
    def form_valid(self, form):
        # form.instance here would be == SomeModel.objects.get(pk=pk_from_kwargs)
        form.instance.save()
        return ...
4b9b3361

Ответ 1

После некоторого обсуждения с вами я все еще не понимаю, почему вы не можете использовать UpdateView. Это кажется очень простым случаем, если я правильно понимаю. У вас есть модель, которую вы хотите обновить. У вас есть специальная форма для очистки, прежде чем сохранять ее в этой модели. Кажется, что UpdateView будет работать отлично. Вот так:

class SaveForm(ModelForm):
    somedata = forms.CharField(required=False)

    class Meta:
        model = SomeModel  # with attr somedata
        fields = ('somedata', 'someotherdata')

    def clean_somedata(self):
        return sometransformation(self.cleaned_data['somedata'])


class SaveView(UpdateView):
    template_name = 'sometemplate.html'
    form_class = SaveForm
    model = SomeModel

    # That should be all you need. If you need to do any more custom stuff 
    # before saving the form, override the `form_valid` method, like this:

    def form_valid(self, form):
        self.object = form.save(commit=False)

        # Do any custom stuff here

        self.object.save()

        return render_to_response(self.template_name, self.get_context_data())

Конечно, если я не понимаю вас, сообщите мне. Вы должны уметь заставить это работать.

Ответ 2

Для других посетителей этой темы да, вы можете сделать FormView, который действует как как CreateView, так и UpdateView. Это, несмотря на некоторые мнения других пользователей, может иметь большой смысл, если вы хотите иметь одиночную форму/URL/страницу для веб-формы для сохранения некоторых пользовательских данных, которые могут быть необязательными, но должны сохраняться один раз и только один раз. Вы не хотите иметь 2 URL/представления для этого, а только одну страницу/URL-адрес, который показывает форму, заполненную предыдущими данными, которые должны быть обновлены, если модель уже была сохранена пользователем.

Подумайте в виде "контактной" модели, подобной этой:

from django.conf import settings
from django.db import models


class Contact(models.Model):
    """
    Contact details for a customer user.
    """
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    street = models.CharField(max_length=100, blank=True)
    number = models.CharField(max_length=5, blank=True)
    postal_code = models.CharField(max_length=7, blank=True)
    city = models.CharField(max_length=50, blank=True)
    phone = models.CharField(max_length=15)
    alternative_email = models.CharField(max_length=254)

Итак, вы пишете ModelForm для него, например:

from django import forms

from .models import Contact


class ContactForm(forms.ModelForm):
    class Meta:
        model = Contact
        exclude = ('user',)  # We'll set the user later.

И ваш FormView с возможностями "создать" и "обновить" будет выглядеть так:

from django.core.urlresolvers import reverse
from django.views.generic.edit import FormView

from .forms import ContactForm
from .models import Contact


class ContactView(FormView):
    template_name = 'contact.html'
    form_class = ContactForm
    success_url = reverse('MY_URL_TO_REDIRECT')

    def get_form(self, form_class):
        """
        Check if the user already saved contact details. If so, then show
        the form populated with those details, to let user change them.
        """
        try:
            contact = Contact.objects.get(user=self.request.user)
            return form_class(instance=contact, **self.get_form_kwargs())
        except Contact.DoesNotExist:
            return form_class(**self.get_form_kwargs())

    def form_valid(self, form):
        form.instance.user = self.request.user
        form.save()
        return super(ContactView, self).form_valid(form)

Вам даже не нужно использовать pk в URL этого примера, потому что объект извлекается из БД через поле user one-to-one. Если у вас есть случай, подобный этому, в котором создаваемая/обновляемая модель имеет уникальные отношения с пользователем, это очень просто.

Надеюсь, это поможет кому-то...

Приветствия.

Ответ 3

Вы можете использовать метод post FormView для получения опубликованных данных и сохранения в модели с помощью form.save(). Надеюсь, это поможет.

Попробуйте это

    class SaveForm(ModelForm):
    somedata = forms.CharField(required=False)

    class Meta:
        model = SomeModel  # with attr somedata
        fields = ('somedata', 'someotherdata')

    def __init__(self, *args, **kwargs):
        super(SaveForm, self).__init__(*args, **kwargs)

    def save(self, id):
        print id   #this id will be sent from the view
        instance = super(SaveForm, self).save(commit=False)
        instance.save()
        return instance


class SaveView(FormView):
    template_name = 'sometemplate.html'
    form_class = SaveForm

    def post(self, request, *args, **kwargs):

        form = self.form_class(request.POST)

        if form.is_valid():
            form.save(kwargs.get('pk'))
        else:
            return self.form_invalid(form)