ุฅุฏุงุฑุฉ ุงู„ุญู‚ูˆู‚ ุนู„ู‰ ู…ุณุชูˆู‰ ุงู„ู…ู†ุดุฃุฉ

ุนุงุฌู„ุงู‹ ุฃู… ุขุฌู„ุงู‹ ุŒ ูŠูˆุงุฌู‡ ู…ุทูˆุฑ 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'),
        )

, , . , , . , , - , .


โ€” , โ€” :


  • Vehicle
  • Driver
  • Order

, , , -.


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',

    # SHOULD BE ALWAYS LAST!
    '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


All Articles