This commit is contained in:
ibuler 2023-04-23 16:15:27 +08:00
parent a105748a55
commit c991a73632
2 changed files with 111 additions and 2 deletions

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import json
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models from django.db import models
@ -32,6 +31,7 @@ __all__ = [
"PortRangeField", "PortRangeField",
"BitChoices", "BitChoices",
"TreeChoices", "TreeChoices",
"JSONManyToManyField",
] ]
@ -274,3 +274,105 @@ class PortRangeField(models.CharField):
kwargs['max_length'] = 16 kwargs['max_length'] = 16
super().__init__(**kwargs) super().__init__(**kwargs)
self.validators.append(PortRangeValidator()) self.validators.append(PortRangeValidator())
from django.db.models import Q
from django.apps import apps
from django.db import models
from django.core.exceptions import ValidationError
import json
class JSONManyToManyDescriptor:
def __init__(self, field):
self.field = field
self._is_setting = False
def __get__(self, instance, owner=None):
if instance is None:
return self
if not hasattr(instance, "_related_manager_cache"):
instance._related_manager_cache = {}
current_value = getattr(instance, self.field.attname, {})
if self.field.name not in instance._related_manager_cache or instance._related_manager_cache[
self.field.name]._is_value_stale(current_value):
manager = RelatedManager(instance, self.field)
instance._related_manager_cache[self.field.name] = manager
return instance._related_manager_cache[self.field.name]
def __set__(self, instance, value):
if instance is None:
return
if not hasattr(instance, "_is_setting"):
instance._is_setting = {}
if self.field.name not in instance._is_setting or not instance._is_setting[self.field.name]:
instance._is_setting[self.field.name] = True
manager = self.__get__(instance, instance.__class__)
manager.set(value)
serialized_value = manager.serialize()
instance.__dict__[self.field.attname] = serialized_value
instance._is_setting[self.field.name] = False
class JSONManyToManyField(models.JSONField):
def __init__(self, related_model, *args, **kwargs):
self.related_model = related_model
super().__init__(*args, **kwargs)
def contribute_to_class(self, cls, name, **kwargs):
super().contribute_to_class(cls, name, **kwargs)
setattr(cls, self.name, JSONManyToManyDescriptor(self))
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
kwargs['related_model'] = self.related_model
return name, path, args, kwargs
def validate(self, value, model_instance):
super().validate(value, model_instance)
if not isinstance(value, list) or not all(isinstance(item, int) for item in value):
raise ValidationError("Invalid JSON data for JSONManyToManyField.")
class RelatedManager:
def __init__(self, instance, field):
self.instance = instance
self.field = field
def _is_value_stale(self, current_value):
return self.serialize() != current_value
def set(self, value):
self.field.value = value
def serialize(self):
return self.field.value
def _get_queryset(self):
model = apps.get_model(self.field.to)
value = self.field.value
if value["type"] == "all":
return model.objects.all()
elif value["type"] == "ids":
return model.objects.filter(id__in=value["ids"])
elif value["type"] == "attrs":
filters = Q()
for attr in value["attrs"]:
if attr["match"] == "exact":
filters &= Q(**{attr["attr"]: attr["value"]})
return model.objects.filter(filters)
def all(self):
return self._get_queryset()
def filter(self, *args, **kwargs):
queryset = self._get_queryset()
return queryset.filter(*args, **kwargs)

View File

@ -2,12 +2,19 @@ from django.db import models
from django.db.models import F, TextChoices from django.db.models import F, TextChoices
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from assets.models import Asset, Node, FamilyMixin
from accounts.models import Account from accounts.models import Account
from assets.models import Asset, Node, FamilyMixin
from common.db.fields import JSONManyToManyField
from common.utils import lazyproperty from common.utils import lazyproperty
from orgs.mixins.models import JMSOrgBaseModel from orgs.mixins.models import JMSOrgBaseModel
class TestPermission2(models.Model):
name = models.CharField(max_length=128, verbose_name=_('Name'))
users = JSONManyToManyField("users.User")
assets = JSONManyToManyField("assets.Asset")
class NodeFrom(TextChoices): class NodeFrom(TextChoices):
granted = 'granted', 'Direct node granted' granted = 'granted', 'Direct node granted'
child = 'child', 'Have children node' child = 'child', 'Have children node'