I ask for help with the task.
There is a notification model. I want to create an asynchronous task for creating notifications. But I get an error Object of type MPTTModelBase is not JSON serializable.
models.py
class Comment(MPTTModel):
"""Модель комментариев"""
content_type = models.ForeignKey(ContentType, verbose_name=_('Тип контента'), related_name='content_ct_%(class)s', on_delete=models.CASCADE)
object_id = models.PositiveIntegerField(verbose_name=_('ID контента'), db_index=True)
content_object = GenericForeignKey('content_type', 'object_id')
"""Род.коммент"""
parent = TreeForeignKey('self', on_delete=models.CASCADE, verbose_name=_('Родительский комментарий'), blank=True,
null=True, related_name='children')
"""Инфо, привязка, модерация"""
content = models.TextField(verbose_name=_('Комментарий'))
created_by = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='comment_created_by', verbose_name=_('Автор комментария'))
is_published = models.BooleanField(verbose_name=_('Опубликовать'), default=True)
time_create = models.DateTimeField(auto_now_add=True, verbose_name=_('Дата добавления'))
"""Generic FK"""
rating = GenericRelation('Rating', related_query_name='%(class)s')
notification = GenericRelation('Notification', related_query_name='%(class)s')
def save(self, *args, **kwargs):
send_create_notification.delay(self, 3)
super().save(*args, **kwargs)
services.py
def create_notification(instance, type):
"""Notification create"""
from modules.core.models import Notification
model_object = instance
obj = model_object.content_object
try:
text = model_object.content[0:120]
except:
text = None
try:
to_user = obj.created_by
except:
to_user = obj
from_user = model_object.created_by
now = timezone.now()
last_minute = now - datetime.timedelta(seconds=60)
similar_actions = Notification.objects.filter(from_user=from_user, to_user=from_user, type=type, time_create__gte=last_minute)
if obj:
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get_for_model(obj)
similar_actions = similar_actions.filter(content_type=content_type, object_id=obj.id)
if not similar_actions:
if text:
notification = Notification(from_user=from_user, to_user=to_user, type=type, content_object=obj, text=text)
else:
notification = Notification(from_user=from_user, to_user=to_user, type=type, content_object=obj)
notification.save()
return True
return False
tasks.py
#shared_task()
def send_create_notification(self, type):
return create_notification(self, type)
send_create_notification.delay(self, 3) will try to serialize your Comment instance, which is not JSON serializable. You could use a pickle serializerbut it's not recommended.
Instead I suggest you to send the comment id as an argument to the task:
send_create_notification.delay(self.pk, 3)
and get the instance on the task
#shared_task()
def send_create_notification(comment_id, type):
comment = Comment.objects.get(pk=comment_id)
return create_notification(comment, type)
Related
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 using django-simple-history to log changes in my models. I have managed to save log of changes, but I always get None on field history_user.
I am following this tutorial.
This is my model:
class Reservation(models.Model):
UNCONFIRMED = 'UNCONFIRMED'
CONFIRMED = 'CONFIRMED'
CANCELED = 'CANCELED'
NO_SHOW = 'NO SHOW'
GO_SHOW = 'GO SHOW'
STATUS_CHOICES = (
(UNCONFIRMED, _('Unconfirmed')),
(CONFIRMED, _('Confirmed')),
(CANCELED, _('Canceled')),
(NO_SHOW, _('No show')),
(GO_SHOW, _('Go show')),
)
booking = models.CharField(max_length=25, verbose_name=_('Booking'))
agency = models.ForeignKey(Agency, on_delete=models.PROTECT, related_name='reservations', verbose_name=_('Agency'))
comment = models.TextField(null=True, blank=True, verbose_name=_('Comment'))
status = models.CharField(max_length=15, choices=STATUS_CHOICES, default=UNCONFIRMED, verbose_name=_('Status'))
confirmation_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Confirmation Date"))
arrival_date = models.DateField(verbose_name=_('Arrival Date'))
departure_date = models.DateField(verbose_name=_('Departure Date'))
confirmation = models.CharField(max_length=15, null=True, blank=True, verbose_name=_('Confirmation Code'))
is_invoiced = models.BooleanField(default=False, verbose_name=_('Is invoiced?'))
euroamerica = models.BooleanField(default=False, verbose_name=_("Is Euroamerica sale"))
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT, related_name='reservations')
changed_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT)
timestamp = models.DateTimeField(null=True, blank=True, auto_now_add=True)
handle_fee = models.FloatField(null=True, blank=True, verbose_name=_("Handle Fee"))
log = HistoricalRecords(related_name='history')
class Meta:
verbose_name = _('Reservation')
verbose_name_plural = _('Reservations')
permissions = (
('reservation_can_edit', 'Can Edit Reservation'),
('reservation_can_view', 'Can View Reservation'),
('reservation_can_view_history_log', 'Can View Reservation History Log')
)
def __str__(self):
return "[{}]{}".format(self.booking, self.agency)
def clean(self, *args, **kwargs):
if self.id is None:
try:
Reservation.objects.get(booking=self.booking)
except:
pass
else:
raise CustomValidation(_('Booking already exists.'), 'booking', status_code=status.HTTP_400_BAD_REQUEST)
if isinstance(self.arrival_date, str):
raise CustomValidation(_('Arrival date must be a valid date.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)
if isinstance(self.departure_date, str):
raise CustomValidation(_('Departure date must be a valid date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)
if self.arrival_date >= self.departure_date:
raise CustomValidation(_('Departure date must be later than Arrival date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)
# elif self.arrival_date <= timezone.datetime.now().date():
# if self.id == None:
# raise CustomValidation(_('Arrival date must be later than today.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)
if self.status == 'CONFIRMED' and self.confirmation is None:
raise CustomValidation(_('Must provide a confirmation number.'), 'confirmation', status_code=status.HTTP_400_BAD_REQUEST)
return True
def save(self, *args, **kwargs):
from GeneralApp.models import Parameter
self.booking = self.booking.replace(' ', '')
self.booking = self.booking.upper()
self.full_clean()
if self.confirmation is not None and self.status is not self.CONFIRMED:
if self.pk is not None:
try:
previous_reservation = Reservation.objects.get(id=self.pk)
except:
pass
else:
if previous_reservation.status == self.status and previous_reservation.confirmation != self.confirmation:
self.status = self.CONFIRMED
else:
self.status = self.CONFIRMED
if self.status == self.CONFIRMED and self.confirmation_date is None:
self.confirmation_date = timezone.now()
try:
self.handle_fee = Parameter.objects.get(name="Handle Fee").value
except:
self.handle_fee = None
super(Reservation, self).save(*args, **kwargs)
#property
def _history_user(self):
return self.changed_by
#_history_user.setter
def _history_user(self, value):
self.changed_by = value
I don't know what I am missing to get the user logged.
EDIT
Here is where I save Reservation:
def save_reservation(self, reservation_data, user, pk):
try:
reservation_to_save = models.Reservation.objects.get(booking=pk)
except:
raise CustomValidation(_("There is not such reservation {}".format(pk)), 'id', status.HTTP_400_BAD_REQUEST)
reservation_to_save.booking = reservation_data['booking']
reservation_to_save.agency = models.Agency.objects.get(id=reservation_data['agency'])
reservation_to_save.comment = reservation_data.get('comment', None)
reservation_to_save.status = reservation_data.get('status', 'UNCONFIRMED')
reservation_to_save.arrival_date = reservation_data['arrival_date']
reservation_to_save.departure_date = reservation_data['departure_date']
reservation_to_save.confirmation = reservation_data.get('confirmation', None)
reservation_to_save.is_invoiced = reservation_data.get('is_invoiced', False)
reservation_to_save.euroamerica = reservation_data.get('euroamerica', False)
reservation_to_save.save()
return reservation_to_save
According to the documentation you have to add HistoryRequestMiddleware to your middleware in settings.py
Like so:
MIDDLEWARE = [
# ...
'simple_history.middleware.HistoryRequestMiddleware',
]
This at least was the solution in my case.
If you don't want to use this middleware you can set the user manually, see this.
I can't figure out why disconnect of post_save signal does not work in my project.
When I call PipedriveSync.objects.first().sync_with_pipedrive(), it does something and then tries to save some information to the object. Then, the sync_pipedrivesync receiver is called which calls sync_with_pipedrive() again which invokes sync_pipedrivesync etc etc.
To avoid this cycling I've created two methods - disconnect_sync and connect_sync which should disconnect signal when saving instance and then revoke it. But it doesn't work.
I can see in debugger that the last line in sync_with_pipedrive - self.save(disconnect=True) invokes the signal.
Do you know what is wrong?
models.py
class PipedriveSync(TimeStampedModel):
pipedrive_id = models.IntegerField(null=True, blank=True)
last_sync = models.DateTimeField(null=True, blank=True)
last_sync_response = JSONField(null=True, blank=True)
last_sync_success = models.NullBooleanField()
last_backsync = models.DateTimeField(null=True, blank=True)
last_backsync_response = JSONField(null=True, blank=True)
last_backsync_success = models.NullBooleanField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def save(self,disconnect=True,**kwargs):
if disconnect:
PipedriveSync.disconnect_sync()
super().save(**kwargs)
PipedriveSync.connect_sync()
#classmethod
def disconnect_sync(cls):
post_save.disconnect(sync_pipedrivesync)
#classmethod
def connect_sync(cls):
post_save.connect(sync_pipedrivesync,sender=PipedriveSync)
def sync_with_pipedrive(self):
if self.pipedrive_id:
action = 'update'
else:
action = 'create'
relative_url = self.build_endpoint_relative_url(action)
payload = self.get_serializer_class()(self.content_object).data
response = None
if action == 'create':
response = PipeDriveWrapper.post(relative_url, payload=payload)
if response['success']:
self.pipedrive_id = response['data']['id']
if action == 'update':
response = PipeDriveWrapper.put(relative_url, payload=payload)
try:
success = response['success']
except KeyError:
success = False
self.last_sync_success = success
self.last_sync_response = response
self.last_sync = now()
self.save(disconnect=True)
#receiver(post_save, sender=PipedriveSync)
def sync_pipedrivesync(instance, sender, created, **kwargs):
instance.sync_with_pipedrive()
I'm trying to map a foreign key to POST data when creating a new object through a serializer. There are two foreign keys in the object, one is serializing perfectly, the other is creating an error.
Model:
class Event(models.Model):
owner = models.ForeignKey('auth.User', related_name='owner', blank=True)
date = models.DateField('eventdate')
time = models.TimeField('eventtime', default=now)
eventtype = models.ForeignKey(EventType, related_name='eventtype', blank=True)
# duration = models.DurationField()
location = models.CharField(max_length=200, blank=True)
attenders = models.ManyToManyField(User, related_name='attenders')
invited = models.ManyToManyField(User, related_name='invitedlist')
View:
class EventMixin(RetrieveUpdateDestroyAPIView, CreateAPIView):
serializer_class = EventSerializer
def get_queryset(self):
return Event.objects.all()
def partial_update(self, request, *args, **kwargs):
request['owner'] = request.user
sname = request['eventtype']
request['eventtype'] = EventType.objects.filter(sname=sname)
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
return super(EventMixin, self).partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs):
new = {}
new['owner'] = request.user.__dict__
new['date'] = request.data['date']
new['time'] = request.data['time']
new['location'] = request.data['location']
sname = request.data['eventtype']
new['eventtype'] = EventType.objects.get(sname=sname).__dict__
json_str = json.dumps(self.request.data)
data = json.loads(json_str)
serializer = EventMixinSerializer(data=new)
with open('/tmp/log.txt', 'w+') as f:
f.write(str(serializer.is_valid()))
f.write(str(serializer.validated_data))
f.close()
serializer.is_valid();
serializer.save()
try:
invited = list(data['toInvite'])
for i in invited:
for j in User.objects.filter(username=i):
invite = EventInvite(invited=j, sender=request.user, event=self.get_object())
invite.save()
self.get_object().invited.add()
except KeyError:
pass
Serializer:
class EventMixinSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
eventtype = EventTypeSerializer()
attenders = FriendsListingField(read_only=True)
invited = FriendsListingField(read_only=True)
class Meta:
model = Event
fields = ('owner', 'eventtype', 'date', 'time', 'location', 'id', 'attenders', 'invited')
def create(self, validated_data):
owner = validated_data.pop('owner')
owner = owner.instance
eventtype = validated_data.pop('eventtype')
eventtype = eventtype.instance
event = Event.objects.create(owner=owner, eventtype=eventtype, **validated_data)
event.save()
return event
Error when owner field present:
False
{'owner': OrderedDict([('username', ['A user with that username already exists.'])])}
Result when UserSerializer(read_only=True) (pretty much diabling it):
True
OrderedDict([('eventtype', OrderedDict([('lname', 'Swimming'), ('sname', 'SWM'), ('category', '1')])), ('date', datetime.date(2015, 12, 22)), ('$
(Notice the event type data in the result)
Thanks!
You need to remove the validators from UserSerializer.
Assuming UserSerializer is a User ModelSerializer it'll extract the unique constraint on the User.username from the Model and your validation will fail.
To work this around you'll need to remove the UniqueValidator by overriding the validators list for the username field of the UserSerializer
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()