ุนุงุฌูุงู ุฃู
 ุขุฌูุงู ุ ููุงุฌู ู
ุทูุฑ Django ู
ุดููุฉ: ููููุฉ ุฌุนููุง ุจุญูุซ ูุง ูู
ูู ููู
ุณุชุฎุฏู
ูู ุชุนุฏูู ุฃู ุญุฐู ุ ุฃู ุญุชู ุนุฏู
 ุฑุคูุฉ ุ ูุงุฆูุงุช ู
ุฎุชููุฉ ู
ู ููุณ ุงูููุน.
ูููุชุฑุถ ุฃู ู
ุดุฑูุนู ูุชุนูู ุจุชุฎุฒูู ู
ุนููู
ุงุช ุงูู
ุดุฑูุน. ูููู
 ุงูู
ุณุชุฎุฏู
ูู ุงูู
ุฎุชูููู ุจุฅุฏุฎุงู ู
ุดุงุฑูุน ู
ุฎุชููุฉ ููุฌุจ ุฃูุง ูุดุงูุฏูุง ู
ุนููู
ุงุช ุญูู ู
ุดุฑูุน ุขุฎุฑ. ูู
ูู ููู
ุณุชุฎุฏู
 ุงููุงุญุฏ ูุงูู
ุณุชุฎุฏู
 ููุณู ุฅุฏุฎุงู ุงูุนุฏูุฏ ู
ู ุงูู
ุดุงุฑูุน ููููู ููู
ุง ุญุงูุฉ ู
ุฎุชููุฉ ูู ู
ุดุฑูุนุงุช ู
ุฎุชููุฉ - ูู ู
ูุงู ู
ุง ูู
ููู ููุท ุนุฑุถ ุงูู
ุนููู
ุงุช ุ ููู ุบูุฑูุง - ุชุญุฑูุฑ ุงูุจูุงูุงุช. ูู ุจุนุถ ุงูู
ุดุงุฑูุน ุ ูุชู
 ุชุณุฌูู ุงูู
ุณุชุฎุฏู
 ูู
ูุธู ู
ุดุฑูุน ุ ููู ู
ุดุฑูุน ุขุฎุฑ ุ ูู
ุณุชููู ูุฎุฏู
ุงุชู ููุท. ูุฌุจ ุฃู ูููู ู
ุณุชูู ุงููุตูู ู
ุฎุชูููุง ุชู
ุงู
ูุง.
ููุงู ุงูุนุฏูุฏ ู
ู ุงูุญุฒู
 ุงูู
ุดุงุฑูุฉ ูู ูุฐู ุงููุถุงูุง ุ ุณููุธุฑ ูู ูุงุญุฏุฉ ู
ููุง - Django-Access . ูู ู
ู ููู
ู ุงูุฃู
ุฑ ู
ุฏุนู ูููุทุท.
ู
ุซุงู
ุชุฃู
ู ูู ุงูู
ุซุงู ุงูู
ูุตูู ุจุฅูุฌุงุฒ ูู ุงูุฏูุจุงุฌุฉ. ููุชุฑุถ ุฃู ูุฏููุง ูุธุงู
ูุง ุจุณูุทูุง ูุฅุฏุงุฑุฉ ุงูุจูุงูุงุช ููู
ุดุงุฑูุน ุฐุงุช ุงุชุฌุงู ู
ุนูู ุ ุนูู ุณุจูู ุงูู
ุซุงู ุ ุงูููู. ู
ู ุญูุซ ุงููุตูู ุฅูู ุงูุจูุงูุงุช ุงูู
ุฎุชููุฉ ุ ูุชู
 ุชูุณูู
 ู
ุณุชุฎุฏู
ู ุงููุธุงู
 ุฅูู ุนุฏุฉ ูุฆุงุช:
- ุงูู
ุณุคูู - ูู
ููู ู
ุนุงูุฌุฉ ุงูุจูุงูุงุช ุงูู
ุชุนููุฉ ุจู
ููู ุงูู
ูุธููู ุชุฌุงู ุงูู
ุดุฑูุน ุ ุงูุฐู ูุฏูุฑ ุงูู
ุณุชุฎุฏู
ูู ููุชุตู ุจูู
 ูููุตููู
 ุนู ุงูู
ุดุฑูุน ุ ููุบูุฑ ุญุงูุฉ ุงููุตูู ูู ุงูู
ุดุฑูุน
- ุงูู
ูุธููู - ูู
ูููู
 ุฅุฏุงุฑุฉ ุจูุงูุงุช ุงูู
ุดุฑูุน ุจุฃูู
ูู: ุฅูุดุงุก ูุญุฐู ูุชุบููุฑ - ุฌู
ูุน ุงูุจูุงูุงุช ุ ุจุงุณุชุซูุงุก ุงูุจูุงูุงุช ุงูู
ุชุนููุฉ ุจู
ููู ุงูู
ูุธููู ู
ู ุงูู
ุดุฑูุน
- ุงูู
ุฑุงุฌุน - ูู
ููู ุนุฑุถ ุจูุงูุงุช ุงูู
ุดุฑูุน ุ ูููู ูุง ูู
ููู ุชุบููุฑูุง
- ุงูุนู
ูู - ูู
ููู ููุท ุนุฑุถ ุงูุณุฌูุงุช ุงูู
ุชุนููุฉ ุจุงูุชูุงุนู ู
ุนู ุดุฎุตููุง ุ ููุฐูู ุชุบููุฑ ุงูุจูุงูุงุช ุงูุดุฎุตูุฉ ูู ู
ููู ุงูุดุฎุตู
ููููุฉ ุงูุชูุงุนู ู
ุน ูุธุงู
 ุญููู Django
, Django, django.contrib.auth. , Permission Group , , .
โ Django django-admin startproject sample.
Django, sample sample options .
- trans python manage.py startapp trans .
Project.
class Project(models.Model):
    name = models.CharField(
        max_length=256,
        db_index=True,
        verbose_name=_('Name'),
        help_text=_('Name of the project')
    )
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = _('Project')
        verbose_name_plural = _('Projects')
. ProjectMember, .
class ProjectMember(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
        verbose_name=_('User'), help_text=_('User who belongs to this project'),
        related_name='projects',
    )
    project = models.ForeignKey(
        Project, on_delete=models.CASCADE,
        verbose_name=_('Project'), help_text=_('Project to which this user belongs'),
        related_name='users',
    )
    allow_change = models.BooleanField(
        default=True,
        verbose_name=_('Allow Change'),
        help_text=_('Is the member allowed to change the project data?')
    )
    allow_manage = models.BooleanField(
        default=False,
        verbose_name=_('Allow Manage'),
        help_text=_('Is the member allowed to manage users for the project?')
    )
    def __str__(self):
        return _('%s -> %s') % (self.user, self.project)
    class Meta:
        verbose_name = _('Project Member')
        verbose_name_plural = _('Project Members')
        unique_together = (
            ('user', 'project'),
        )
, , . , , . , , - , .
โ , โ :
, , , -.
class Vehicle(models.Model):
    project = models.ForeignKey(
        Project, on_delete=models.CASCADE,
        verbose_name=_('Project'), help_text=_('Project to which this vehicle belongs'),
        related_name='vehicles',
    )
    code = models.CharField(
        max_length=256,
        db_index=True,
        verbose_name=_('Code'),
        help_text=_('Registration code')
    )
    def __str__(self):
        return self.code
    class Meta:
        verbose_name = _('Vehicle')
        verbose_name_plural = _('Vehicles')
        unique_together = (
            ('code', 'project'),
        )
class Driver(models.Model):
    project = models.ForeignKey(
        Project, on_delete=models.CASCADE,
        verbose_name=_('Project'), help_text=_('Project to which this driver belongs'),
        related_name='drivers',
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
        verbose_name=_('User'), help_text=_('User who is a driver'),
        related_name='driving',
    )
    def __str__(self):
        return _('%s [%s]') % (self.user, self.project)
    class Meta:
        verbose_name = _('Driver')
        verbose_name_plural = _('Drivers')
        unique_together = (
            ('user', 'project'),
        )
class Order(models.Model):
    project = models.ForeignKey(
        Project, on_delete=models.CASCADE,
        verbose_name=_('Project'), help_text=_('Project to which this order belongs'),
        related_name='orders',
    )
    client = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
        verbose_name=_('Client'), help_text=_('Client getting an order'),
        related_name='orders',
    )
    vehicle = models.ForeignKey(
        Vehicle, on_delete=models.CASCADE,
        verbose_name=_('Vehicle'), help_text=_('Vehicle assigned to this order'),
        related_name='orders',
    )
    driver = models.ForeignKey(
        Driver, on_delete=models.CASCADE,
        verbose_name=_('Driver'), help_text=_('Driver assigned to this order'),
        related_name='orders',
    )
    start_time = models.DateTimeField(
        db_index=True,
        verbose_name=_("Start Time"),
        help_text=_("Time when the order is started")
    )
    stop_time = models.DateTimeField(
        db_index=True,
        verbose_name=_("Stop Time"),
        help_text=_("Time when the order is stopped")
    )
    def __str__(self):
        return _('%(start_time)s-%(stop_time)s %(client)s %(driver)s %(vehicle)s') % {
            'start_time': self.start_time,
            'stop_time': self.stop_time,
            'client': self.client,
            'driver': self.driver,
            'vehicle': self.vehicle,
        }
    class Meta:
        verbose_name = _('Order')
        verbose_name_plural = _('Orders')
access trans settings.py, python makemigrations trans python migrate.
, , admin.py trans. Django-Access.
?. -, Django-Access , , , Django, , , .
-, , , Django-Access, , Django, .
 @admin.register(Project)
class ProjectAdmin(AccessModelAdmin):
    fields = ['name']
    list_display = ['name']
    search_fields = ['name']
@admin.register(ProjectMember)
class ProjectMemberAdmin(AccessModelAdmin):
    fields = ['user', 'project', 'allow_manage', 'allow_change']
    list_display = ['user', 'project', 'allow_manage', 'allow_change']
    search_fields = ['user__username', 'user__first_name', 'user__last_name', 'project__name']
    list_filters = ['project', 'allow_manage', 'allow_change']
    autocomplete_fields = ['user', 'project']
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "project":
            projects = AccessManager(Project).changeable(request)
            kwargs["queryset"] = projects
        return super(ProjectMemberAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
@admin.register(Vehicle)
class VehicleAdmin(AccessModelAdmin):
    fields = ['project', 'code']
    list_display = ['code', 'project']
    list_filters = ['project']
    search_fields = ['code']
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "project":
            projects = Project.objects.filter(users__user=request.user, users__allow_change=True)
            kwargs["queryset"] = projects
        return super(VehicleAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
@admin.register(Driver)
class DriverAdmin(AccessModelAdmin):
    fields = ['project', 'user']
    list_display = ['user', 'project']
    list_filters = ['project']
    search_fields = ['user__username', 'user__first_name', 'user__last_name', 'project__name']
    autocomplete_fields = ['user', 'project']
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "project":
            projects = Project.objects.filter(users__user=request.user, users__allow_change=True)
            kwargs["queryset"] = projects
        return super(DriverAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
@admin.register(Order)
class OrderAdmin(AccessModelAdmin):
    fields = ['project', 'client', 'vehicle', 'driver', 'start_time', 'stop_time']
    list_display = ['start_time', 'stop_time', 'client', 'driver', 'vehicle', 'project']
    list_filters = ['project', 'vehicle', 'driver']
    search_fields = ['client__username', 'client__first_name', 'client__last_name', 'project__name',]
    autocomplete_fields = ['client', 'vehicle', 'driver', 'project']
    date_hierarchy = 'start_time'
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "project":
            projects = Project.objects.filter(users__user=request.user, users__allow_change=True)
            kwargs["queryset"] = projects
        return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
, .
, .
, , autocomplete:
admin.site.unregister(User)
admin.site.unregister(Group)
@admin.register(User)
class AccessUserAdmin(AccessControlMixin, UserAdmin):
    def get_readonly_fields(self, request, obj=None):
        readonly_fields = super(AccessUserAdmin, self).get_readonly_fields(request, obj) or []
        if request.user.is_superuser:
            return readonly_fields
        if not obj:
            return readonly_fields
        restrict = ['is_superuser', 'last_login', 'date_joined']
        if obj.pk != request.user.pk:
            restrict = ['is_superuser', 'last_login', 'date_joined', 'password', 'email']
        return [f for f in readonly_fields if f not in restrict] + restrict
    def get_list_display(self, request):
        fields = super(AccessUserAdmin, self).get_list_display(request) or []
        if request.user.is_superuser:
            return fields
        restrict = ['password', 'email']
        return [f for f in fields if not f in restrict]
    def _fieldsets_exclude(self, fieldsets, exclude):
        ret = []
        for nm, params in fieldsets:
            if 'fields' not in params:
                ret.append((nm, params))
                continue
            fields = []
            for f in params['fields']:
                if f not in exclude:
                    fields.append(f)
            pars = {}
            pars.update(params)
            pars['fields'] = fields
            ret.append((nm, pars))
        return ret
    def _fieldsets_only(self, fieldsets, only):
        ret = []
        for nm, params in fieldsets:
            if 'fields' not in params:
                ret.append((nm, params))
                continue
            fields = []
            for f in params['fields']:
                if f in only:
                    fields.append(f)
            pars = {}
            pars.update(params)
            pars['fields'] = fields
            ret.append((nm, pars))
        return ret
    def get_fieldsets(self, request, obj=None):
        fieldsets = list(super(AccessUserAdmin, self).get_fieldsets(request, obj)) or []
        fields = self.get_fields(request, obj=obj)
        return self._fieldsets_only(fieldsets, fields)
    def get_fields(self, request, obj=None):
        fields = list(super(AccessUserAdmin, self).get_fields(request, obj)) or []
        exclude = ['is_staff', 'groups', 'user_permissions']
        if not request.user.is_superuser:
            exclude = ['is_staff', 'is_superuser', 'groups', 'user_permissions']
            if obj and obj.pk != request.user.pk:
                exclude = ['is_staff', 'password', 'email', 'groups', 'user_permissions']
        return [f for f in fields if not f in exclude]
    def save_model(self, request, obj, form, change):
        obj.is_staff = True
        return super(AccessUserAdmin, self).save_model(request, obj, form, change)
, , , ( is_staff is_superuser), , , .
, authorize. , , .
?, . authorize INSTALLED_APPS settings.py, .
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'access',
    'trans',
    
    'authorize',
]
 python manage.py startapp authorize models.py. , .
?, models.py, , , , INSTALLED_APPS. , , .
 , , ( ) , . , , , .
, , , . , , , , . , , .
AccessManager.register_plugins({
    User: CompoundPlugin(
        CheckAblePlugin(
            appendable=(lambda model, request: 
                {} if request.user.id and request.user.projects.filter(allow_manage=True) else
                False
            ),
        ),
        ApplyAblePlugin(
            visible=(
                lambda queryset, request:
                    queryset if request.user.projects.filter(
                        allow_manage=True
                    ) else queryset.filter(
                        projects__project__users__user=request.user.id
                    ).distinct() or queryset.filter(
                        id=request.user.id
                    ).distinct()
            ),
            changeable=(lambda queryset, request: queryset.filter(id=request.user.id).distinct()),
            deleteable=(lambda queryset, request: queryset.filter(id=request.user.id).distinct())
        )
    ),
})
.
-, , , , , . , , , , .
-, , , . , , .
AccessManager.register_plugins({
    Project: CompoundPlugin(
        CheckAblePlugin(
            appendable=(lambda model, request: False),
        ),
        ApplyAblePlugin(
            visible=(lambda queryset, request: queryset.filter(
                users__user=request.user.id
            ).distinct()),
            changeable=(lambda queryset, request: queryset.filter(
                users__user=request.user.id,
                users__allow_manage=True
            ).distinct()),
            deleteable=(lambda queryset, request: queryset.none()),
        )
    ),
    ProjectMember: CompoundPlugin(
        CheckAblePlugin(
            appendable=(lambda model, request: {} if request.user.id and request.user.projects.filter(allow_manage=True) else False),
        ),
        ApplyAblePlugin(
            visible=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id
            ).distinct()),
            changeable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_manage=True
            ).distinct()),
            deleteable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_manage=True
            ).distinct()),
        )
    ),
})
, , . , , allow_change.
, โ , , () .
, , :
- Driverโ ,
- Orderโ , , , , .
.
AccessManager.register_plugins({
    Vehicle: CompoundPlugin(
        ApplyAblePlugin(
            visible=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id
            ).distinct()),
            changeable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_change=True
            ).distinct()),
            deleteable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_change=True
            ).distinct()),
        )
    ),
    Driver: CompoundPlugin(
        ApplyAblePlugin(
            visible=(lambda queryset, request: queryset.filter(
                Q(project__users__user=request.user.id) | Q(user=request.user)
            ).distinct()),
            changeable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_change=True
            ).distinct()),
            deleteable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_change=True
            ).distinct()),
        )
    ),
    Order: CompoundPlugin(
        ApplyAblePlugin(
            visible=(lambda queryset, request: queryset.filter(
                Q(
                    project__users__user=request.user.id
                ) | Q(
                    client=request.user
                ) | Q(
                    driver__user=request.user
                )
            ).distinct()),
            changeable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_change=True
            ).distinct()),
            deleteable=(lambda queryset, request: queryset.filter(
                project__users__user=request.user.id,
                project__users__allow_change=True
            ).distinct()),
        )
    ),
})
?, .
, Django-Access , , .
- , . , . project, , .
ProjectMember:
    ...
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "project":
            projects = AccessManager(Project).changeable(request)
            kwargs["queryset"] = projects
        return super(ProjectMemberAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
:
    ...
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "project":
            projects = Project.objects.filter(users__user=request.user, users__allow_change=True)
            kwargs["queryset"] = projects
        return super(..., self).formfield_for_foreignkey(db_field, request, **kwargs)
( โฆ ).
 . , Django, Django-Access , , Django. access.plugins.DjangoAccessPlugin, , .
settings.py. , , , , .
class SuperOnly(AccessPluginBase):
    def check_visible(self, model, request):
        return request.user.is_superuser and {}
    def check_changeable(self, model, request):
        return request.user.is_superuser and {}
    def check_appendable(self, model, request):
        return request.user.is_superuser and {}
    def check_deleteable(self, model, request):
        return request.user.is_superuser and {}
settings.py
ACCESS_DEFAULT_PLUGIN = "authorize.models.SuperOnly"
, , , .
ุจุงุณุชุฎุฏุงู
 ุญุฒู
ุฉ Django-Access ุ ูู
ูุง ุจุตูุงุบุฉ ุฌู
ูุน ุงูููุงุนุฏ ุงูุถุฑูุฑูุฉ ุชูุฑูุจูุง ุ ูุชู
 ุชูููุฐ ุงูุชูุงุตูู ุงูู
ุชุจููุฉ ูุชูููุฏ ุงููุตูู ู
ู ุฎูุงู ุชุบููุฑุงุช ุตุบูุฑุฉ ูู ููุญุฉ ุงูู
ุดุฑู.
ูู
ูุง ุจุตูุงุบุฉ ุฌู
ูุน ููุงุนุฏ ุชูููุฏ ุงููุตูู ูู ุดูู ุงุณุชูุณุงุฑุงุช ุฅูู DBMS.
ูู
ููู ุฑุคูุฉ ุดูุฑุฉ ุงูู
ุตุฏุฑ ุงููุงู
ูุฉ ููุชุทุจูู ููุฏ ุงูุชุดุบูู ุนูู ู
ุณุชูุฏุน GitHub