Задать вопрос
@kicherov_maxim

Как сделать вычисляемое поле в модели Django?

Добрый день. Необходимо сделать вычисляемое поле в модели Django. Пытаюсь переопределить метод save для этого.

from django.db import models
import tlsh
from django.contrib import admin

# Create your models here.


class Font(models.Model):
    name = models.CharField(max_length=500, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["name"]


class Language(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["name"]


class FingerPrint(models.Model):

    def result_string(self):
        return self.user_agent + str(self.time_zone) + str(self.screen_h) + str(self.screen_w) + \
               str(self.hardware_concurrency) + ''.join(lng.name for lng in self.browser_languages.all()) + \
               self.canvas_fingerprint + '1'.join(font.name for font in self.installed_fonts.all()) + \
               '0'.join(font.name for font in self.not_installed_fonts.all())

    def calculate_tlsh(self):
        return tlsh.forcehash(self.result_string().encode())

    user_agent = models.CharField(max_length=1000,blank=True)
    time_zone = models.IntegerField(blank=True)
    screen_w = models.IntegerField(blank=True)
    screen_h = models.IntegerField(blank=True)
    hardware_concurrency = models.IntegerField(blank=True)
    browser_languages = models.ManyToManyField('Language', related_name='browser_languages')
    canvas_fingerprint = models.CharField(max_length=256, blank=True)
    installed_fonts = models.ManyToManyField(Font, related_name='installed_fonts')
    not_installed_fonts = models.ManyToManyField('Font', related_name='not_installed_fonts')
    tlsh = models.CharField(max_length=400, blank=True)

    def save(self, *args, **kwargs):
        #super(FingerPrint, self).save(*args, **kwargs) костыль который чинит ошибку
        self.tlsh = self.calculate_tlsh()
        print(':', self.tlsh)
        super(FingerPrint, self).save(*args, **kwargs)


Получаю ошибку
File "/home/maxim/PycharmProjects/fingerprint/core/fingerprinter/models.py", line 52, in save
    self.tlsh = self.calculate_tlsh()
  File "/home/maxim/PycharmProjects/fingerprint/core/fingerprinter/models.py", line 37, in calculate_tlsh
    return tlsh.forcehash(self.result_string().encode())
  File "/home/maxim/PycharmProjects/fingerprint/core/fingerprinter/models.py", line 33, in result_string
    self.canvas_fingerprint + '1'.join(font.name for font in self.installed_fonts.all()) + \
  File "/home/maxim/PycharmProjects/fingerprint/venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 535, in __get__
    return self.related_manager_cls(instance)
  File "/home/maxim/PycharmProjects/fingerprint/venv/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 848, in __init__
    (instance, self.pk_field_names[self.source_field_name]))
ValueError: "<FingerPrint: FingerPrint object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.


я так понял что это происходит из за того что сначало нужно сохранить объект а потом только обращаться к ManyToManyFields. и если в методе save сначало сохранить, потом вычислить поле, потом опять сохранить то все работает. Как грамотно сделать данную задачу?
  • Вопрос задан
  • 3103 просмотра
Подписаться 1 Простой Комментировать
Решения вопроса 1
@Realmixer
Full stack Python (Django) web-developer
Например, через сигналы:
@receiver(post_save, sender=FingerPrint)
def update_calculated_fields(sender, instance, **kwargs):
    tlsh = instance.calculate_tlsh()
    sender.objects.filter(pk=instance.pk).update(tlsh=tlsh)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы