I have two django Models: PageModel and RecordModel. They are registered in django admin panel. I want automatically create RecordModel object and assign it to (object of PageModel).record field if record is not selected in process of Page creation. (Django Admin - Add Page form) I tried to create form and used clean_record() method, but that not work. (debugger is not stopped there) How can i solve the problem?
Models (Sortable and SortableAdmin classes are part of adminsortable (https://github.com/iambrandontaylor/django-admin-sortable), but I think it does not really matter):
class Record(Sortable):
"""
Запись в книге почетных гостей
"""
class Meta(Sortable.Meta):
verbose_name = u'Запись'
verbose_name_plural = u'Записи'
author = models.CharField(verbose_name=u'Автор', max_length=255, unique=False, blank=False,
default=author_default)
def __unicode__(self):
return u'Запись {} ({})'.format(self.id, self.author)
class Page(Sortable):
"""
Страница книги почетных гостей
"""
class Meta(Sortable.Meta):
verbose_name = u'Страница'
verbose_name_plural = u'Страницы'
record = SortableForeignKey(Record, verbose_name=u'Запись', related_name='pages', blank=False, default=None)
image = models.ImageField(verbose_name=u'Картинка',
upload_to='pages',
default='',
help_text=u'Размер файла - до 10 MB. Формат PNG.',
validators=[ImageValidator(formats=['PNG'], max_size=10000000)])
updated = models.DateTimeField(verbose_name=u'Обновление', auto_now=True, null=True,
help_text=u'Время последнего изменения страницы на сервере')
def __unicode__(self):
return u'Страница {} ({})'.format(self.id, self.image)
Admin:
class PageInline(SortableTabularInline):
model = Page
#admin.register(Record)
class RecordAdmin(SortableAdmin):
list_display = ['author', 'pages_count']
inlines = [PageInline]
fields = ['author']
def pages_count(self, object):
return object.pages.count()
pages_count.short_description = u'Количество страниц'
pages_count.allow_tags = False
class PageAdminForm(forms.ModelForm):
def clean_record(self):
return self.cleaned_data["record"]
#admin.register(Page)
class PageAdmin(SortableAdmin):
list_display = ['__unicode__', 'image', 'author', 'updated']
form = PageAdminForm
readonly_fields = ['updated']
def get_fields(self, request, obj=None):
if obj:
return super(PageAdmin, self).get_fields(request, obj)
else:
return ['record', 'image']
def author(self, page):
return page.record.author
author.allow_tags = False
author.short_description = u'Автор записи'
I solved the problem as follows:
Change record field in Page model
record = SortableForeignKey(Record, verbose_name=u'Запись', related_name='pages', null=True, blank=True, default=None)
Add save_model() method to PageAdmin
def save_model(self, request, obj, form, change):
if obj.record is None:
record = Record.objects.create(author=BookConfiguration.get_solo().record_author_default + ' (' + timezone.localtime(timezone.now()).strftime('%Y-%m-%d %H:%M:%S') + ')')
record.save()
Related
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.
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.
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 = 'Настройки'
models.py
class Rooms(models.Model):
objects = None
room_num = models.IntegerField(verbose_name='Комната')
room_bool = models.BooleanField(default=True,verbose_name='Релевантность')
category = models.CharField(max_length=150,verbose_name='Категория')
price = models.CharField(max_length=105,verbose_name='Цена (сум)')
def __str__(self):
return f'{self.room_num}'
class Meta:
verbose_name = 'Комнату'
verbose_name_plural = 'Комнаты'
class Registration(models.Model):
objects = None
rooms = models.ForeignKey(Rooms, on_delete=models.CASCADE,verbose_name='Номер',help_text='Номер в который хотите заселить гостя!')
first_name = models.CharField(max_length=150,verbose_name='Имя')
last_name = models.CharField(max_length=150,verbose_name='Фамилия')
admin = models.ForeignKey(User, on_delete=models.CASCADE,verbose_name='Администратор')
pasport_serial_num = models.CharField(max_length=100,verbose_name='Серия паспорта',help_text='*AB-0123456')
birth_date = models.DateField(verbose_name='Дата рождения')
img = models.FileField(verbose_name='Фото документа',help_text='Загружайте файл в формате .pdf')
visit_date = models.DateTimeField(
default=datetime.datetime(year=year, month=month, day=day, hour=datetime.datetime.now().hour,
minute=datetime.datetime.now().minute, second=00,),verbose_name='Дата прибытия')
leave_date = models.DateTimeField(
default=datetime.datetime(year=year, month=month, day=day + 1, hour=12, minute=00, second=00),verbose_name='Дата отбытия')
guest_count = models.IntegerField(default=1,verbose_name='Кол-во людей')
room_bool = models.BooleanField(default=False,verbose_name='Релевантность',help_text='При бронирование отключите галочку')
price = models.CharField(max_length=105,default='Появится после сохранения!',verbose_name='Цена (сум)')
def __str__(self):
return f'{self.rooms},{self.last_name},{self.first_name},{self.room_bool}'
class Meta:
verbose_name = 'Номер'
verbose_name_plural = 'Регистрация'
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Registration
#receiver(post_save, sender=Registration)
def create_profile(sender, instance, created, **kwargs):
if created:
instance.rooms.room_bool = False
instance.rooms.save()
this is the code in the signals that updates the value from another model according to the value of another WHEN I CREATE (if in model 1 it will be False, then in model 2 it will also be False) how can I make it so that when EDITING changes the values
I recommend use pre_save signal for this situation
#receiver(pre_save, sender=Registration)
def create_profile(instance, *args, **kwargs):
if instance.pk #Its update
else: #Its create
django.db.models.signals.pre_save
This is sent at the beginning of a model’s save() method.
django.db.models.signals.post_save
Like pre_save but sent at the end of the save() method
your model will not has id when creating so you can catch with pk instance is data for your model
#receiver(post_save, sender=Registration)
def create_profile(sender, instance, created,**kwargs):
if created:
instance.rooms.room_bool = instance.room_bool
instance.rooms.save()
instance.price = instance.rooms.price
instance.save()
elif not created:
instance.rooms.room_bool = instance.room_bool
instance.rooms.save()
I am getting FieldError as :
Unknown field(s) (notedate) specified for AssistantNotes
When i call the page. It throws this error. I am using Django 1.9.5 and python 2.7.
I have notedate field in the AssistantNotes table in my db. If i delete "notedate" from modelformset_factory row in my view, it works. I couldnt solve why it is not showing notedate although it is in DB and in model. And generating error. The field is already in the model.
My view is :
def edit_assistant_notes(request):
isassistantsuperadmin = getUserPermissions(request) #Yes if 1, no if 0
list = getUserType(request)
userisassistant = list[2]
if userisassistant == "YES" or isassistantsuperadmin ==1:
list = getUserType(request)
type = list[0]
usertype = list[1] #"Nöbetçi Muavin":1 , "Yetkili":2
if request.method == 'GET':
if AssistantNotes.objects.filter(notedate=nicosia_date(datetime.today()).date()).count() == 0:
AssistantNotesFormsetFactory = modelformset_factory(AssistantNotes, fields=('time', 'notedate', 'categories', 'type', 'dailynote',))
else:
AssistantNotesFormsetFactory = modelformset_factory(AssistantNotes, fields=('time', 'notedate', 'categories', 'type', 'dailynote',), can_delete=True)
if usertype == 1:
formset = AssistantNotesFormsetFactory(queryset=AssistantNotes.objects.filter(notedate=nicosia_date(datetime.today()).date(), type=type))
elif usertype == 2:
formset = AssistantNotesFormsetFactory(queryset=AssistantNotes.objects.all().order_by("notedate", "time"))
helper = TableInlineHelper()
return render(request, 'edit-assistant-notes.html', {'formset': formset, 'helper': helper})
My model is :
class AssistantNotes(BaseModel):
categories = models.CharField(choices=CATEGORIES, default="GENERAL", max_length=100, verbose_name=_("CAT"))
time = models.CharField(choices=TIME, default="-------------", max_length=20, verbose_name=_("Time"))
dailynote = models.TextField(null=True, blank=True, verbose_name=_("Add Note"))
writer = models.TextField(null=True, blank=True, verbose_name=_("Adder"))
notedate = models.DateField(auto_now_add=True, db_index=True, verbose_name=_("Date"))
type = models.CharField(choices=SCHOOLTYPE, default="---", max_length=100, verbose_name=_("SchoolType"))
def __unicode__(self):
return "%s / %s" % (self.dailynote, self.categories)
class Meta:
ordering = ['dailynote']
How can i force this field to be editable ?
Instead of setting auto_now_add, you may override the save() method of the model, say
class AssistantNotes(BaseModel):
....
notedate = models.DateField(db_index=True, verbose_name=_("Date"))
def save(self, *args, **kwargs):
if not self.id:
self.notedate = timezone.now()
return super(AssistantNotes, self).save(*args, **kwargs)
When trying to add a second appointment (for the same date) which has a dayplan foreign key using ModelForm and CreateView, unique constraint fails due to DayPlan having 'date' field as unique.
This issue is not present using the django-admin create form.
I tried to remove the unique=True from dayplan.date to see what happens -> every time i add an appointment, even if dayplan.date exist, a new dayplan is created.
the issue seems to be related to these 2 line:
daydate = DayPlan.objects.filter(date=planned_date)
form.cleaned_data['dayplan'] = daydate
The code is here:
class DayPlan(models.Model):
date = models.DateField(unique=True, db_index=True)
comment = models.TextField(null=True, blank=True)
def __str__(self):
return 'Planning voor {}'.format(self.date)
def get_absolute_url(self):
return reverse('organizer_dayplan_detail', kwargs={'pk': self.pk})
class Appointment(models.Model):
comment = models.CharField(null=True, blank=True, max_length=255)
planned_date = models.DateField()
doctor = models.ForeignKey(Doctor)
visited = models.BooleanField(default=False)
dayplan = models.ForeignKey(DayPlan)
class AppointCreate(CreateView):
model = Appointment
form_class = AppointmentForm
template_name = 'organizer/organizer_appointment_create.html'
# initial = {'doctor': 'pk', 'comment': 'test',}
def get_initial(self):
return {
"doctor": self.request.GET.get('doctor')
}
def form_valid(self, form):
planned_date = form.cleaned_data['planned_date']
try:
daydate = DayPlan.objects.filter(date=planned_date)
form.cleaned_data['dayplan'] = daydate
form.instance.save()
except:
daydate = DayPlan.objects.create(date=planned_date)
form.instance.dayplan = daydate
form.instance.save()
return super(AppointCreate, self).form_valid(form)
class AppointmentForm(forms.ModelForm):
class Meta:
model = Appointment
fields = {'comment', 'planned_date', 'doctor', 'visited', 'dayplan'}
widgets = {'visited': forms.HiddenInput(),}
exclude = {'dayplan',}
P.S. i do realize that i don't need to use "form.instance.save()" here. removing them has no effect.
Thanks in advance!
solved
daydate, created = DayPlan.objects.get_or_create(date=planned_date)
form.instance.dayplan = DayPlan.objects.get(date=planned_date)
The admin.py is as follows :-
class SiteDetailInline(admin.TabularInline):
model = SiteDetail
form = SiteDetailForm
fields = ('name', )
can_delete = False
extra = 1
max_num = 1
def get_readonly_fields(self, request, obj=None):
if obj:
return ('clmsid',) + self.readonly_fields
return self.readonly_fields
class SiteAdmin(admin.ModelAdmin):
inlines = [ SiteDetailInline, ]
def queryset(self, queryset):
return Site.objects.filter(~Q(id = settings.SITE_ID))
signals.post_save.connect(create_sites_default_user, sender=Site)
admin.site.unregister(Site)
admin.site.register(Site, SiteAdmin)
The models.py is as follows :-
class SiteDetail(models.Model):
name = models.CharField(max_length=100, unique=True)
client = models.ForeignKey(client)
site = models.ForeignKey(Site)
clmsid = models.CharField(max_length=15, unique=True, verbose_name='clms id', help_text='clms identifier', ) # unique identifier L-XXXXXX-id
def save(self, *args, **kwargs):
if "L-" != self.clmsid[:2]:
self.clmsid = "%s-%s-%s" % ("L", self.accountid, self.id)
super(SiteDetail, self).save(*args, **kwargs)
def __unicode__(self):
return u''
I want to show the extra site details inline in the admin for the site framework. It is not giving any error. However the site details are not displayed inline. Please let me know, what mistake am I doing. Thanks in advance.
Try this
def get_fields(self, request, obj=None):
if obj:
return ('clmsid',) + self.fields
return self.fields