LINUX.ORG.RU

instance содержит не все поля при обработке в ModelForm

 ,


0

3

Имеются две сущности: Server и Server_page. Обе содержат поля name, которые должны быть уникальны.

Форма из модели Server_page работает как надо.

Модель:

class Server_page(models.Model):
    server = models.ForeignKey(Server)
    name = models.CharField(max_length=16, validators=[RegexValidator(regex='[a-z0-9_]+', message=_('You can use only lower case letters of the English alphabet, digits and a sign "_".'))])
    title = models.CharField(max_length=64)
    content = models.CharField(max_length=4096)
    sort_number = models.IntegerField(default=0)
Форма:
class ServerPageCreationForm(forms.ModelForm):
    captcha = CaptchaField()
    class Meta:
        model = Server_page
        fields = ['name', 'title', 'content', 'sort_number']
        widgets = {
          'content': forms.Textarea
        }
    def clean_name(self):
        data = self.cleaned_data['name']
        try:
            another_server_page = Server_page.objects.get(name=data)
        except:
            another_server_page = None
        if another_server_page == None or another_server_page.id == self.instance.id:
            return data
        else:
            raise forms.ValidationError(_('Server page with same name is exist already.'))

class ServerPageEditingForm(ServerPageCreationForm):
    def __init__(self, *args, **kwargs):
        super(ServerPageEditingForm, self).__init__(*args, **kwargs)
        del self.fields['captcha']

Аналогичный подход с Server не работает. Модель:

class Server(models.Model):
    game = models.ForeignKey(Game)
    user = models.ForeignKey(User)
    name = models.CharField(unique = True, max_length=16, validators=[RegexValidator(regex='[a-z0-9_]+', message=_('You can use only lower case letters of the English alphabet, digits and a sign "_".'))])
    title = models.CharField(max_length=64)
    password = models.CharField(max_length=64)
    public_state = models.IntegerField(default=0)
    creating_timestamp = models.DateTimeField(auto_now_add=True)
    updating_timestamp = models.DateTimeField(auto_now_add=True)
    ip_address = models.GenericIPAddressField(verbose_name='Ip address')
    port = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(65535)])
Форма:
class ServerCreationForm(forms.ModelForm):
    captcha = CaptchaField()
    class Meta:
        model = Server
        fields = ['game', 'name', 'title', 'password', 'ip_address', 'port']
    def clean_name(self):
        data = self.cleaned_data['name']
        try:
            another_server = Server.objects.get(name=data)
        except:
            another_server = None
        if another_server == None or another_server.id == self.instance.id:
            return data
        else:
            raise forms.ValidationError(_('Server with same name is exist already.'))

class ServerEditingForm(ServerCreationForm):
    def __init__(self, *args, **kwargs):
        super(ServerEditingForm, self).__init__(*args, **kwargs)
        del self.fields['game']
        del self.fields['captcha']
Проблема заключается в том, что при работе с формой ServerEditingForm в вызываемом методе предка clean_name self.instance.id равен None. Хотя форма создается следующим образом:
ServerEditingForm(instance=server)

Перемещено true_admin из development


self.instance.id равен None

id появляется только после сохранения модели.

Давно не работал с джанговскими формами, но... Разве unique=True не достаточно чтобы форма всё делала сама как надо?

Server_page

Лучше назови ServerPage.

data = self.cleaned_data['name']

Лучше назови переменную name, будет понятнее тем кто код читает.

unique = True

Пробелы в аргументах не ставят. В общем, прогони программу через pyflakes и autopep8, код будет лучше.

UP: вот тут про id написано: https://docs.djangoproject.com/en/1.9/ref/models/instances/#auto-incrementing...

true_admin ★★★★★ ()
Последнее исправление: true_admin (всего исправлений: 1)

Там нет instance. Можно id добавить в __init__ как-то так -

if 'instance' in kwargs: self.id = kwargs['instance'].id

Но лучшу эту проверку отдать modelform - пускай этот класс сам этим занимается, потом del .. поле - это вообще ты откуда такое придумал? добавь exclude например.

elagagarina ()
Ответ на: комментарий от true_admin

Лучше назови переменную name, будет понятнее тем кто код читает.

здесь как раз все норм. Документация также предлагает.

Но других косяков полно. Половина телодвижений бессмысленна.

elagagarina ()
Ответ на: комментарий от elagagarina

При создании сущности Server self.instanc.id равен None, потому проверка проходит как надо: проверяется только наличие сервера с таким же именем. Когда производится редактирование сервера форме передаётся instance. У неё есть id. Все должно работать так как и с Server_page. Ситуации аналогичны.

Задание уникальности в модели не подходит, так как невозможно будет отредактировать существующую сущность оставив name неизменным. Будет ругаться на нарушение уникальности. Потому я и использовала такое изящное решение.

totik ()
Ответ на: комментарий от totik

self.instanc.id равен None, потому проверка проходит как надо:

он всегда будет None.

Т.к. в self формы нет instance. Я тебе предложила вариант решения. Плюс проверку на неуникальность нужно не так делать. Но это ладно.

так как невозможно будет отредактировать существующую сущность оставив name неизменным.

Разве? Через админку точно работает, через modelform - не могу сейчас проверить, но в админке modelform используется - и все ок, все редактируется.

elagagarina ()
Ответ на: комментарий от elagagarina

здесь как раз все норм. Документация также предлагает.

Хз, мне осмысленные имена переменных больше нравятся.

true_admin ★★★★★ ()
Ответ на: комментарий от true_admin

Отсюда -

Хотя форма создается следующим образом:

ServerEditingForm(instance=server)

elagagarina ()
Ответ на: комментарий от true_admin

Тут осмысленная переменная, в том смысле, что есть куча методов clean_bla_bla...

а data - просто данные которые мы проверяем, во всех методах.

elagagarina ()
Ответ на: комментарий от elagagarina

Я ещё раз обращаются ваше внимание на то, что при редактировании модели в конструктор формы передаётся instance, уже созданный объект. В случае с Server_page это работает.

totik ()
Ответ на: комментарий от totik

Т.е. если вместо ServerEditingForm подсунуть ServerCreationForm то всё работает?

Не факт что поможет, но ты попробуй вместо del ... сделать вот так:

class ServerEditingForm(ServerCreationForm):
  fields = [f for f in ServerCreationForm.fields if f not in "game captcha".split()]

Так нужно делать хотя бы потому что у джанги много магии (или было много магии) в классах и ты во многих случаях не можешь вот так просто взять и в __init__ «наколдовать».

Плюс, убедись что у тебя актуальная версия джанги. А то в ней бывают баги которые со временем фиксят.

true_admin ★★★★★ ()
Ответ на: комментарий от true_admin

Проблема при использовании ServerEditingForm, в который передаётся instance. В нём есть все данные модели кроме id. Django 1.9

totik ()
Ответ на: комментарий от true_admin

fields = [f for f in ServerCreationForm.fields if f not in «game captcha».split()]

есть exclude для этого.

elagagarina ()
Ответ на: комментарий от true_admin

Проверяла. Печатала принтом перед передачей в форму. Id был. А вот. Методе уже None.

totik ()
Ответ на: комментарий от totik

Удали class ServerEditingForm(ServerCreationForm):

т.к. на сколько я понимаю ты его используешь только для удаления полей формы. Используй exclude для этого.

Ну и проверку как-то так делай, раз уж так не хочется стандартные методы использовать -

try:
            another_server = Server.objects.get(name=data).exclude(pk=self.instance.pk)
        except Server.DoesNotExist:
...
elagagarina ()
Ответ на: комментарий от elagagarina

Я не совсем поняла, как мне использую один и тот же класс формы в одном случае отображать одни поля, а в другом случае другие.

Для задания поля exclude мне все равно нужно наследовать класс, разве не так?

totik ()
Ответ на: комментарий от elagagarina

Changed in Django 1.6: Before version 1.6, the '__all__' shortcut did not exist, but omitting the fields attribute had the same effect. Omitting both fields and exclude is now deprecated, but will continue to work as before until version 1.8

totik ()
Ответ на: комментарий от totik

А ну если поля только в этой форме убирать - то да, все таки наследовать придется. Но __init__ не переопределяй.

elagagarina ()
Ответ на: комментарий от totik

Ну и что? Опускать и fields и exclude - типа будет нельзя. Ну так не опускай эти параметры.

elagagarina ()

Всем спасибо за помощь, я нашла свою ошибку. Оказывается, если форма невалидна, я вызывала тот же шаблон, передавая в контекст форму, забыв передать instance.

В общем, было два места, где форма создается. Одно я привела вам в качестве примера, забыв про второе.

Тогда уж в догонку еще один вопрос: как мне сохранить в существующую модель только те поля, которые были изменены в форме?

Есть код:

    form = ServerEditingForm(request.POST if request.method == 'POST' else None, instance=server)
    if form.is_valid():
        server_from_form = form.save(commit=False)
        server.title = server_from_form.title
        server.password = server_from_form.password
        server.ip_address = server_from_form.ip_address
        server.port = server_from_form.port
        server.updating_timestamp = datetime.now()
        server.save()
Каким образом можно обойтись без перебирания всех полей?
    if form.is_valid():
        form.save()
в моем случае приводит к ошибке 'NoneType' object has no attribute 'id' Да и server.updating_timestamp тоже необходимо обновлять. Автообновление поля в модели подходит, так как это единственное место, где это поле должно обновляться. В других случаях я тоже могу править этот объект без обновления поля.

totik ()
Ответ на: комментарий от totik

как мне сохранить в существующую модель только те поля, которые были изменены в форме?

Почему ты не хочешь использовать form.save() и сохранить всё и сразу?

А, вижу, оно не работает. Погугли, на стэковерфлоу у людей были похожие проблемы.

true_admin ★★★★★ ()
Последнее исправление: true_admin (всего исправлений: 1)
Ответ на: комментарий от totik

Капец. Ну серьезно.

'NoneType' object has no attribute 'id'

<ванга-моде>у тебя идет обращение в instance.id при создании объекта до его сохранения в базу.</ванга-моде>

form.save() - все что нужно, ошибки из-за того что ты накрутила не нужные/кривые проверки.

elagagarina ()
Ответ на: комментарий от true_admin

Потому что у нее это -

в моем случае приводит к ошибке 'NoneType' object has no attribute 'id'

а к id она обращается, проверяя на уникальность записи (что не нужно и сделано неправильно)

elagagarina ()
Ответ на: комментарий от elagagarina

form.save() - все что нужно, ошибки из-за того что ты накрутила не нужные/кривые проверки.

А как правильно?

Server.name - уникально для всей таблицы. Server_page.name - уникально только для своего сервера.

В итоге это меня удовлетворяет:

class ServerCreationForm(forms.ModelForm):
    captcha = CaptchaField()

    class Meta:
        model = Server
        fields = ['game', 'name', 'title', 'password', 'ip_address', 'port']

    def clean_name(self):
        data = self.cleaned_data['name']
        try:
            another_server = Server.objects.get(name=data)
        except ObjectDoesNotExist:
            another_server = None
        if another_server == None or another_server.id == self.instance.id:
            return data
        else:
            raise forms.ValidationError(_('Server with same name is exist already.'))

class ServerEditingForm(ServerCreationForm):
    def __init__(self, *args, **kwargs):
        super(ServerEditingForm, self).__init__(*args, **kwargs)
        del self.fields['game']
        del self.fields['captcha']

class ServerPageCreationForm(forms.ModelForm):
    captcha = CaptchaField()
    class Meta:
        model = Server_page
        fields = ['name', 'title', 'content', 'sort_number']
        widgets = {
          'content': forms.Textarea
        }
    def clean_name(self):
        data = self.cleaned_data['name']
        try:
            another_server_page = Server_page.objects.get(name=data,server=self.instance.server)
        except ObjectDoesNotExist:
            another_server_page = None
        if another_server_page == None or another_server_page.id == self.instance.id:
            return data
        else:
            raise forms.ValidationError(_('Server page with same name is exist already.'))

class ServerPageEditingForm(ServerPageCreationForm):
    def __init__(self, *args, **kwargs):
        super(ServerPageEditingForm, self).__init__(*args, **kwargs)
        del self.fields['captcha']
Даже для Server не удается использовать атрибут уникальности в модели, так как при редактировании формы проверка валидности возвращает нарушение уникальности.

totik ()
Ответ на: комментарий от totik

Даже для Server не удается использовать атрибут уникальности в модели, так как при редактировании формы проверка валидности возвращает нарушение уникальности.

Не может быть, если все правильно сделано.

elagagarina ()
Ответ на: комментарий от totik

Нужен актуальный код и бэктрейсы чтобы было понятно откуда берётся ошибка. Ну и причеши уже код (хотя бы убрать del).

PS s/is exist already/already exists/g

true_admin ★★★★★ ()
Ответ на: комментарий от elagagarina
class Server_page(models.Model):
    server = models.ForeignKey(Server)
    name = models.CharField(max_length=16, validators=[RegexValidator(regex='[a-z0-9_]+', message=_('You can use only lower case letters of the English alphabet, digits and a sign "_".'))])
    title = models.CharField(max_length=64)
    content = models.CharField(max_length=4096)
    sort_number = models.IntegerField(default=0)
    class Meta:
        unique_together = (('server', 'name'),)

class ServerPageCreationForm(forms.ModelForm):
    captcha = CaptchaField()
    class Meta:
        model = Server_page
        fields = ['name', 'title', 'content', 'sort_number']
        widgets = {
          'content': forms.Textarea
        }

При создании form.is_valid возвращает true. При попытке вставки: (1062, «Duplicate entry '1-info' for key 'ssn_server_page_server_id_1dcc9e8c_uniq'»)

То есть в форме соответствующий валидатор не появился.

totik ()
Ответ на: комментарий от elagagarina

Да, makemigrations, migrate делала.

def hoster_server_page_create_action(request, server_id):
    if not request.user.is_authenticated():
        return HttpResponseRedirect(reverse('ssn:access_forbidden'))
    try:
        server = Server.objects.get(pk = server_id)
    except ObjectDoesNotExist:
        return Http404(_('Server not found.'))
    if server.user != request.user:
        return HttpResponseRedirect(reverse('ssn:access_forbidden'))
    if server.public_state != 0:
        return HttpResponseRedirect(reverse('ssn:hoster_server_public_state_toogle', kwargs={'server_id': server.id}))
    form = ServerPageCreationForm(request.POST if request.method == 'POST' else None, instance=Server_page(server=server))
    if form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse('ssn:hoster_server', kwargs={'server_id': server.id}))
    return render(request, 'ssn/hoster_server_page_create.html', {'form': form, 'server': server})
totik ()
Ответ на: комментарий от elagagarina

Server_page(server=server) - ты новый инстанс создаешь.

Причём, с всего-лишь одним проинициализированным полем, и инициализированным неправильно :).

true_admin ★★★★★ ()
Ответ на: комментарий от elagagarina

Так ведь это и есть создание сервера, а не редактирование. Иначе каким образом мне указать форме ограничение по серверу?

totik ()
Ответ на: комментарий от totik

А, теперь более понятно стало. Путаница в голове с Server/ServerPage

Вот так надо -

form = ServerPageCreationForm(request.POST if request.method == 'POST' else None, initial={'server':server})

Поле server сделать hidden

Или можно в конце сделать так -

    if form.is_valid():
        server_page = form.save(commit=False)
        server_page.server = server // ставим нам сервер
        server_page.save()

elagagarina ()
Ответ на: комментарий от totik

У тебя еще form.save() делается не зависимо от того было сохранение или просто по GET страничку открыли.

Лучше так -


    if request.method == 'POST':
        form = ServerPageCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('ssn:hoster_server', kwargs={'server_id': server.id}))
    else:
        form = ServerPageCreationForm(initial={'server_id':server.id})


elagagarina ()
Ответ на: комментарий от totik

Re: Rap, бит и гитары


Ответ на сообщение:

Посоветуйте исполнителей читающих рэп в сопровождении электрогитар. Что-то вроде Onyx/Biohazard - Judgment Night или Machine Head - Colours.
Как называется это? Рэпкор все-таки что-то другое.

Разное есть, смотря что там доминирует. Если это рэп с гитарами, то это рэпкор. Если это рок, где присутствует рэп читка, то может быть nu-metal типа Limp Bizkit (но не обязательно в nu-metal должен присутствовать рэп) или ранние Linkin Park (до того как они скатились в соплерок для геев).

К примеру, у Run DMC и Beastie Boys полно записей с гитарами, но это хип-хоп с гитарами, какого-то отдельного жанра для них не стали придумывать. Одни из первых начали смешивать рок с рэпом.

Rage Against the Machine хороший пример. Даже не знаю куда их лучше отнести, по мне так это пример хорошей рок группы просто, рэпкором их называть не хочется. Группа культовая.

Ещё годный пример - Body Count.

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.