Как я могу изменить поле на основе другого поля m2m?
Итак, я пытаюсь сделать следующее: установить статус объекта на основе длины поля m2m. Вот как это выглядит
from django.db import models
class Dependency(models.Model):
dependency = models.SlugField('Шаблон')
class Seo(models.Model):
statuses = (
(1, 'Дефолтный'),
(2, 'Дополнительный')
)
dependencies = models.ManyToManyField(
Dependency,
verbose_name='Зависимости',
blank=True,
help_text='Оставьте пустым, если это дефолтный шаблон'
)
h1 = models.CharField('Заголовок(h1)', max_length=200)
title = models.CharField('Заголовок(title)', max_length=200)
description = models.CharField('Описание', max_length=200)
keywords = models.TextField('Ключевые слова')
status = models.IntegerField('Статус', choices=statuses, blank=True, editable=False)
def save(self, *args, **kwargs):
if len(self.dependencies) == 0:
self.status = 1
else:
self.status = 2
# self.status = 1
#
# print(len(self.dependencies))
super().save(*args, **kwargs)
class Page(models.Model):
pass
Но он выдает мне ошибку, которая выглядит так
ValueError: "<Seo: Seo object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
И что я хочу достичь, так это то, что всякий раз, когда поле зависимости пустое, статус должен быть 1, а в противном случае он должен быть 2. Но я не смог найти способ сделать это.
Думаю, вы можете использовать транзакции базы данных Django.
from django.db import transaction
class Seo(models.Model):
...
def save(self, *args, **kwargs):
instance = super(Seo, self).save(*args, **kwargs)
if self.id = None:
transaction.on_commit(self.update_status)
return instance
def update_status(self):
if len(self.dependencies) == 0:
self.status = 1
else:
self.status = 2
self.save()
Надеюсь, это может помочь.
Итак, после нескольких часов гугления и просто плавления функций у меня возникла мысль, которая просто всплыла в моем мозгу, что если я погуглю что-то вроде "django m2m validation" и я получил это. После чтения документации django и этого Django ManyToMany model validation мне удалось заставить его работать.
но все же для меня это действительно безумие, почему в django нет m2m сохранения и валидации по умолчанию. Вот код. admin.py@admin.register(Seo)
class SeoAdmin(admin.ModelAdmin):
form = SEOForm
list_display = [
field.name for field in Seo._meta.get_fields() if field.name not in ['dependencies']
] + ['get_dependencies']
list_display_links = ['id']
search_fields = ['id', 'h1', 'title']
def get_dependencies(self, obj):
if obj:
return ', '.join(d.dependency for d in obj.dependencies.all())
get_dependencies.short_description = 'Зависимости'
forms.py
from django import forms
from django.core.exceptions import ValidationError
from .models import Seo
from .services import SEOService
_seo_service = SEOService()
class SEOForm(forms.ModelForm):
"""
I just wonder why Django doesn't have validation m2m mechanism.
"""
class Meta:
model = Seo
fields = [field.name for field in Seo._meta.get_fields()]
_delimiter = _seo_service.delimiter
def clean(self):
dependencies = self.cleaned_data.get('dependencies')
if dependencies:
fields = [
self.cleaned_data.get('h1'),
self.cleaned_data.get('title'),
self.cleaned_data.get('keywords'),
self.cleaned_data.get('description')
]
dependencies_list = [dep.dependency for dep in dependencies]
for field in fields:
if not _seo_service.is_value_valid(field, dependencies_list, delimiter=self._delimiter):
raise ValidationError(
f'The amount of {self._delimiter} signs is the same as dependencies. '
f'Amount of {self._delimiter}: {field.count(self._delimiter)}. Dependencies: {len(dependencies_list)}. '
f'Field content: {field}.'
)
return self.cleaned_data
def save(self, commit=True):
m = super(SEOForm, self).save(commit=False)
m.save()
self.save_m2m()
if not m.dependencies.all():
m.status = 1
return m
m.status = 2
dependencies_list = [dep.dependency for dep in m.dependencies.all()]
m.h1 = _seo_service.replace_by_delimiter(m.h1, dependencies_list, self._delimiter)
m.title = _seo_service.replace_by_delimiter(m.title, dependencies_list, self._delimiter)
m.description = _seo_service.replace_by_delimiter(m.description, dependencies_list, self._delimiter)
m.keywords = _seo_service.replace_by_delimiter(m.keywords, dependencies_list, self._delimiter)
return m
models.py
from django.db import models
from .services import SEOService
class Dependency(models.Model):
dependency = models.SlugField(
'Зависимость', unique=True,
help_text='Перечислите зависимости через нижнее подчеркивание. Пример: brand_model'
)
def __str__(self) -> str:
return f'Зависимость {self.dependency}'
class Meta:
verbose_name = 'Зависимость'
verbose_name_plural = 'Зависимости'
class Seo(models.Model):
statuses = (
(1, 'Дефолтная'),
(2, 'Дополнительная')
)
_delimiter = SEOService().delimiter
dependencies = models.ManyToManyField(
Dependency,
verbose_name='Зависимости',
blank=True,
help_text='Оставьте пустым, если это дефолтный шаблон.'
)
h1 = models.CharField(
'Заголовок(h1)', max_length=200,
help_text=f'Если вы ввели Купить {_delimiter}, а зависимость - car,'
f' то после сохранения получится Купить car машину. '
f'Все {_delimiter} заменяются на соотв. им зависимости.'
)
title = models.CharField(
'Заголовок(title)', max_length=200,
help_text=f'Если вы ввели Купить {_delimiter}, а зависимость - car,'
f' то после сохранения получится Купить car машину. '
f'Все {_delimiter} заменяются на соотв. им зависимости.'
)
description = models.CharField(
'Описание', max_length=200,
help_text=f'Если вы ввели Купить {_delimiter}, а зависимость - car,'
f' то после сохранения получится Купить car машину. '
f'Все {_delimiter} заменяются на соотв. им зависимости.'
)
keywords = models.TextField(
'Ключевые слова',
help_text=f'Если вы ввели Купить {_delimiter}, а зависимость - car,'
f' то после сохранения получится Купить car машину. '
f'Все {_delimiter} заменяются на соотв. им зависимости.'
)
status = models.IntegerField('Статус', blank=True, choices=statuses, help_text='Не трогать руками', null=True)
def __str__(self) -> str:
return f'Настройка сео'
class Meta:
verbose_name = 'Настройка'
verbose_name_plural = 'Настройки'