So, what i'm tryna do here is set the status of an object based on the length of m2m field. Here's how it looks
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
But it throws me an error that goes like
ValueError: "<Seo: Seo object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
And what it want to achieve is whenever the dependency field is empty then status should be 1 and otherwise it should be 2. But i couldn't find a way to do it.
CodePudding user response:
I think you can use Django database transactions.
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()
Hope it could help.
CodePudding user response:
So, after several hours googling and just melt-functioning i had a thought that just popped right into my brain, what if i google something like "django m2m validation" and i got it. after reading django docs and this Django ManyToMany model validation i managed to get it working.
but still for me it is indeed crazy why django has no m2m sort of saving and validation kinda thing by default. Here goes the code. 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 = 'Настройки'