|
| 1 | +from django.contrib.contenttypes.fields import GenericForeignKey |
| 2 | +from django.contrib.contenttypes.models import ContentType |
| 3 | +from django import forms |
| 4 | +from django.db import models |
| 5 | + |
| 6 | + |
| 7 | +class PictureWidget(forms.MultiWidget): |
| 8 | + def __init__(self): |
| 9 | + widgets = [ |
| 10 | + forms.Select(attrs={},choices=[ |
| 11 | + (ct.id, ct.name) for ct in ContentType.objects.filter(id=45) |
| 12 | + ]), |
| 13 | + forms.NumberInput(attrs={}), |
| 14 | + ] |
| 15 | + super().__init__(widgets) |
| 16 | + |
| 17 | + def decompress(self, value): |
| 18 | + print("Decompress Value: ", value) |
| 19 | + if value: |
| 20 | + return [value.content_type.id, value.object_id] |
| 21 | + return [None, None] |
| 22 | + |
| 23 | + def value_from_datadict(self, data, file, name): |
| 24 | + value = super().value_from_datadict(data, file, name) |
| 25 | + value = ContentType.objects.filter(id=value[0]).first() |
| 26 | + return value |
| 27 | + |
| 28 | + |
| 29 | +class PictureFormField(forms.Field): |
| 30 | + widget = PictureWidget |
| 31 | + |
| 32 | + def __init__(self, *args, **kwargs): |
| 33 | + kwargs.pop('encoder', None) |
| 34 | + kwargs.pop('decoder', None) |
| 35 | + super().__init__(*args, **kwargs) |
| 36 | + |
| 37 | + def prepare_value(self, value): |
| 38 | + print("picture form: ", value) |
| 39 | + # if we return list or tuple from here, |
| 40 | + # django skips decompress |
| 41 | + # value = (4, 3) |
| 42 | + return value |
| 43 | + |
| 44 | + # def to_python(self, value): |
| 45 | + # # This is called inside `clean` |
| 46 | + # # Note that `clean` is called inside `full_clean`'s `_clean_fields()` |
| 47 | + # pass |
| 48 | + |
| 49 | + |
| 50 | +class PictureField(GenericForeignKey, models.JSONField): |
| 51 | + def __init__(self, **kwargs): |
| 52 | + # call to models.JSONField |
| 53 | + super(GenericForeignKey, self).__init__(**kwargs) |
| 54 | + |
| 55 | + # call to GenericForeignKey |
| 56 | + super().__init__(**kwargs) |
| 57 | + self.editable = True |
| 58 | + |
| 59 | + def contribute_to_class(self, cls, name): |
| 60 | + ct = models.ForeignKey(ContentType, |
| 61 | + editable=False, |
| 62 | + on_delete=models.CASCADE) |
| 63 | + fk = models.PositiveIntegerField(editable=False) |
| 64 | + |
| 65 | + self.ct_field = picture_ct = f"{name}_ct" |
| 66 | + self.fk_field = picture_fk = f"{name}_fk" |
| 67 | + |
| 68 | + cls.add_to_class(picture_ct, ct) |
| 69 | + cls.add_to_class(picture_fk, fk) |
| 70 | + super().contribute_to_class(cls, name) |
| 71 | + |
| 72 | + # this is required by _post_clean hook used during |
| 73 | + # model validation in forms |
| 74 | + self.attname = self.name |
| 75 | + |
| 76 | + def formfield(self, **kwargs): |
| 77 | + kwargs.setdefault("form_class", PictureFormField) |
| 78 | + return super().formfield(**kwargs) |
| 79 | + |
0 commit comments