mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 04:55:44 +00:00
Docstring checks in QC checks (#3089)
* Add pre-commit to the stack * exclude static * Add locales to excludes * fix style errors * rename pipeline steps * also wait on precommit * make template matching simpler * Use the same code for python setup everywhere * use step and cache for python setup * move regular settings up into general envs * just use full update * Use invoke instead of static references * make setup actions more similar * use python3 * refactor names to be similar * fix runner version * fix references * remove incidential change * use matrix for os * Github can't do this right now * ignore docstyle errors * Add seperate docstring test * update flake call * do not fail on docstring * refactor setup into workflow * update reference * switch to action * resturcture * add bash statements * remove os from cache * update input checks * make code cleaner * fix boolean * no relative paths * install wheel by python * switch to install * revert back to simple wheel * refactor import export tests * move setup keys back to not disturbe tests * remove docstyle till that is fixed * update references * continue on error * add docstring test * use relativ action references * Change step / job docstrings * update to merge * reformat comments 1 * fix docstrings 2 * fix docstrings 3 * fix docstrings 4 * fix docstrings 5 * fix docstrings 6 * fix docstrings 7 * fix docstrings 8 * fix docstirns 9 * fix docstrings 10 * docstring adjustments * update the remaining docstrings * small docstring changes * fix function name * update support files for docstrings * Add missing args to docstrings * Remove outdated function * Add docstrings for the 'build' app * Make API code cleaner * add more docstrings for plugin app * Remove dead code for plugin settings No idea what that was even intended for * ignore __init__ files for docstrings * More docstrings * Update docstrings for the 'part' directory * Fixes for related_part functionality * Fix removed stuff from merge99676ee
* make more consistent * Show statistics for docstrings * add more docstrings * move specific register statements to make them clearer to understant * More docstrings for common * and more docstrings * and more * simpler call * docstrings for notifications * docstrings for common/tests * Add docs for common/models * Revert "move specific register statements to make them clearer to understant" This reverts commitca96654622
. * use typing here * Revert "Make API code cleaner" This reverts commit24fb68bd3e
. * docstring updates for the 'users' app * Add generic Meta info to simple Meta classes * remove unneeded unique_together statements * More simple metas * Remove unnecessary format specifier * Remove extra json format specifiers * Add docstrings for the 'plugin' app * Docstrings for the 'label' app * Add missing docstrings for the 'report' app * Fix build test regression * Fix top-level files * docstrings for InvenTree/InvenTree * reduce unneeded code * add docstrings * and more docstrings * more docstrings * more docstrings for stock * more docstrings * docstrings for order/views * Docstrings for various files in the 'order' app * Docstrings for order/test_api.py * Docstrings for order/serializers.py * Docstrings for order/admin.py * More docstrings for the order app * Add docstrings for the 'company' app * Add unit tests for rebuilding the reference fields * Prune out some more dead code * remove more dead code Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
"""Admin classes for the 'users' app"""
|
||||
|
||||
from django import forms
|
||||
from django.contrib import admin, messages
|
||||
@ -14,9 +15,7 @@ User = get_user_model()
|
||||
|
||||
|
||||
class RuleSetInline(admin.TabularInline):
|
||||
"""
|
||||
Class for displaying inline RuleSet data in the Group admin page.
|
||||
"""
|
||||
"""Class for displaying inline RuleSet data in the Group admin page."""
|
||||
|
||||
model = RuleSet
|
||||
can_delete = False
|
||||
@ -32,13 +31,13 @@ class RuleSetInline(admin.TabularInline):
|
||||
|
||||
|
||||
class InvenTreeGroupAdminForm(forms.ModelForm):
|
||||
"""
|
||||
Custom admin form for the Group model.
|
||||
"""Custom admin form for the Group model.
|
||||
|
||||
Adds the ability for editing user membership directly in the group admin page.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines extra fields"""
|
||||
model = Group
|
||||
exclude = []
|
||||
fields = [
|
||||
@ -47,6 +46,7 @@ class InvenTreeGroupAdminForm(forms.ModelForm):
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs): # pragma: no cover
|
||||
"""Populate the 'users' field with the users in the current group"""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if self.instance.pk:
|
||||
@ -63,11 +63,11 @@ class InvenTreeGroupAdminForm(forms.ModelForm):
|
||||
)
|
||||
|
||||
def save_m2m(self): # pragma: no cover
|
||||
# Add the users to the Group.
|
||||
|
||||
"""Add the users to the Group"""
|
||||
self.instance.user_set.set(self.cleaned_data['users'])
|
||||
|
||||
def save(self, *args, **kwargs): # pragma: no cover
|
||||
"""Custom save method for Group admin form"""
|
||||
# Default save
|
||||
instance = super().save()
|
||||
# Save many-to-many data
|
||||
@ -76,9 +76,7 @@ class InvenTreeGroupAdminForm(forms.ModelForm):
|
||||
|
||||
|
||||
class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
"""
|
||||
Custom admin interface for the Group model
|
||||
"""
|
||||
"""Custom admin interface for the Group model."""
|
||||
|
||||
form = InvenTreeGroupAdminForm
|
||||
|
||||
@ -90,8 +88,7 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
'stock_item', 'build', 'purchase_order', 'sales_order')
|
||||
|
||||
def get_rule_set(self, obj, rule_set_type):
|
||||
''' Return list of permissions for the given ruleset '''
|
||||
|
||||
"""Return list of permissions for the given ruleset."""
|
||||
# Get all rulesets associated to object
|
||||
rule_sets = RuleSet.objects.filter(group=obj.pk)
|
||||
|
||||
@ -101,6 +98,7 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
break
|
||||
|
||||
def append_permission_level(permission_level, next_level):
|
||||
"""Appen permission level"""
|
||||
if not permission_level:
|
||||
return next_level
|
||||
|
||||
@ -128,30 +126,39 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
return permission_level
|
||||
|
||||
def admin(self, obj):
|
||||
"""Return the ruleset for the admin role"""
|
||||
return self.get_rule_set(obj, 'admin')
|
||||
|
||||
def part_category(self, obj):
|
||||
"""Return the ruleset for the PartCategory role"""
|
||||
return self.get_rule_set(obj, 'part_category')
|
||||
|
||||
def part(self, obj):
|
||||
"""Return the ruleset for the Part role"""
|
||||
return self.get_rule_set(obj, 'part')
|
||||
|
||||
def stock_location(self, obj):
|
||||
"""Return the ruleset for the StockLocation role"""
|
||||
return self.get_rule_set(obj, 'stock_location')
|
||||
|
||||
def stock_item(self, obj):
|
||||
"""Return the ruleset for the StockItem role"""
|
||||
return self.get_rule_set(obj, 'stock')
|
||||
|
||||
def build(self, obj):
|
||||
"""Return the ruleset for the BuildOrder role"""
|
||||
return self.get_rule_set(obj, 'build')
|
||||
|
||||
def purchase_order(self, obj):
|
||||
"""Return the ruleset for the PurchaseOrder role"""
|
||||
return self.get_rule_set(obj, 'purchase_order')
|
||||
|
||||
def sales_order(self, obj):
|
||||
"""Return the ruleset for the SalesOrder role"""
|
||||
return self.get_rule_set(obj, 'sales_order')
|
||||
|
||||
def get_formsets_with_inlines(self, request, obj=None):
|
||||
"""Return all inline formsets"""
|
||||
for inline in self.get_inline_instances(request, obj):
|
||||
# Hide RuleSetInline in the 'Add role' view
|
||||
if not isinstance(inline, RuleSetInline) or obj is not None:
|
||||
@ -160,12 +167,12 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
filter_horizontal = ['permissions']
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
"""
|
||||
This method serves two purposes:
|
||||
"""Save overwrite.
|
||||
|
||||
This method serves two purposes:
|
||||
- show warning message whenever the group users belong to multiple groups
|
||||
- skip saving of the group instance model as inlines needs to be saved before.
|
||||
"""
|
||||
|
||||
# Get form cleaned data
|
||||
users = form.cleaned_data['users']
|
||||
|
||||
@ -186,6 +193,7 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
messages.add_message(request, messages.WARNING, warning_message)
|
||||
|
||||
def save_formset(self, request, form, formset, change):
|
||||
"""Save the inline formset"""
|
||||
# Save inline Rulesets
|
||||
formset.save()
|
||||
# Save Group instance and update permissions
|
||||
@ -193,8 +201,7 @@ class RoleGroupAdmin(admin.ModelAdmin): # pragma: no cover
|
||||
|
||||
|
||||
class InvenTreeUserAdmin(UserAdmin):
|
||||
"""
|
||||
Custom admin page for the User model.
|
||||
"""Custom admin page for the User model.
|
||||
|
||||
Hides the "permissions" view as this is now handled
|
||||
entirely by groups and RuleSets.
|
||||
@ -213,9 +220,7 @@ class InvenTreeUserAdmin(UserAdmin):
|
||||
|
||||
|
||||
class OwnerAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Custom admin interface for the Owner model
|
||||
"""
|
||||
"""Custom admin interface for the Owner model."""
|
||||
pass
|
||||
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
"""DRF API definition for the 'users' app"""
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
@ -14,16 +15,16 @@ from users.serializers import OwnerSerializer, UserSerializer
|
||||
|
||||
|
||||
class OwnerList(generics.ListAPIView):
|
||||
"""
|
||||
List API endpoint for Owner model. Cannot create.
|
||||
"""List API endpoint for Owner model.
|
||||
|
||||
Cannot create.
|
||||
"""
|
||||
|
||||
queryset = Owner.objects.all()
|
||||
serializer_class = OwnerSerializer
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
"""
|
||||
Implement text search for the "owner" model.
|
||||
"""Implement text search for the "owner" model.
|
||||
|
||||
Note that an "owner" can be either a group, or a user,
|
||||
so we cannot do a direct text search.
|
||||
@ -34,7 +35,6 @@ class OwnerList(generics.ListAPIView):
|
||||
It is not necessarily "efficient" to do it this way,
|
||||
but until we determine a better way, this is what we have...
|
||||
"""
|
||||
|
||||
search_term = str(self.request.query_params.get('search', '')).lower()
|
||||
|
||||
queryset = super().filter_queryset(queryset)
|
||||
@ -54,8 +54,9 @@ class OwnerList(generics.ListAPIView):
|
||||
|
||||
|
||||
class OwnerDetail(generics.RetrieveAPIView):
|
||||
"""
|
||||
Detail API endpoint for Owner model. Cannot edit or delete
|
||||
"""Detail API endpoint for Owner model.
|
||||
|
||||
Cannot edit or delete
|
||||
"""
|
||||
|
||||
queryset = Owner.objects.all()
|
||||
@ -63,9 +64,7 @@ class OwnerDetail(generics.RetrieveAPIView):
|
||||
|
||||
|
||||
class RoleDetails(APIView):
|
||||
"""
|
||||
API endpoint which lists the available role permissions
|
||||
for the current user
|
||||
"""API endpoint which lists the available role permissions for the current user.
|
||||
|
||||
(Requires authentication)
|
||||
"""
|
||||
@ -75,7 +74,7 @@ class RoleDetails(APIView):
|
||||
]
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
"""Return the list of roles / permissions available to the current user"""
|
||||
user = request.user
|
||||
|
||||
roles = {}
|
||||
@ -108,7 +107,7 @@ class RoleDetails(APIView):
|
||||
|
||||
|
||||
class UserDetail(generics.RetrieveAPIView):
|
||||
""" Detail endpoint for a single user """
|
||||
"""Detail endpoint for a single user."""
|
||||
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
@ -116,7 +115,7 @@ class UserDetail(generics.RetrieveAPIView):
|
||||
|
||||
|
||||
class UserList(generics.ListAPIView):
|
||||
""" List endpoint for detail on all users """
|
||||
"""List endpoint for detail on all users."""
|
||||
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
@ -135,20 +134,18 @@ class UserList(generics.ListAPIView):
|
||||
|
||||
|
||||
class GetAuthToken(APIView):
|
||||
""" Return authentication token for an authenticated user. """
|
||||
"""Return authentication token for an authenticated user."""
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return self.login(request)
|
||||
|
||||
def delete(self, request):
|
||||
return self.logout(request)
|
||||
|
||||
def login(self, request):
|
||||
"""Return an API token if the user is authenticated
|
||||
|
||||
- If the user already has a token, return it
|
||||
- Otherwise, create a new token
|
||||
"""
|
||||
if request.user.is_authenticated:
|
||||
# Get the user token (or create one if it does not exist)
|
||||
token, created = Token.objects.get_or_create(user=request.user)
|
||||
@ -156,7 +153,8 @@ class GetAuthToken(APIView):
|
||||
'token': token.key,
|
||||
})
|
||||
|
||||
def logout(self, request):
|
||||
def delete(self, request):
|
||||
"""User has requested deletion of API token"""
|
||||
try:
|
||||
request.user.auth_token.delete()
|
||||
return Response({"success": "Successfully logged out."},
|
||||
|
@ -1,14 +1,22 @@
|
||||
"""App configuration class for the 'users' app"""
|
||||
|
||||
import logging
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.utils import OperationalError, ProgrammingError
|
||||
|
||||
from InvenTree.ready import canAppAccessDatabase
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
"""Config class for the 'users' app"""
|
||||
|
||||
name = 'users'
|
||||
|
||||
def ready(self):
|
||||
|
||||
"""Called when the 'users' app is loaded at runtime"""
|
||||
if canAppAccessDatabase(allow_test=True):
|
||||
|
||||
try:
|
||||
@ -22,15 +30,15 @@ class UsersConfig(AppConfig):
|
||||
pass
|
||||
|
||||
def assign_permissions(self):
|
||||
|
||||
"""Update role permissions for existing groups"""
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from users.models import RuleSet, update_group_roles
|
||||
|
||||
# First, delete any rule_set objects which have become outdated!
|
||||
for rule in RuleSet.objects.all():
|
||||
if rule.name not in RuleSet.RULESET_NAMES: # pragma: no cover # can not change ORM without the app beeing loaded
|
||||
print("need to delete:", rule.name)
|
||||
if rule.name not in RuleSet.RULESET_NAMES: # pragma: no cover # can not change ORM without the app being loaded
|
||||
logger.info(f"Deleting outdated ruleset: {rule.name}")
|
||||
rule.delete()
|
||||
|
||||
# Update group permission assignments for all groups
|
||||
@ -39,7 +47,7 @@ class UsersConfig(AppConfig):
|
||||
update_group_roles(group)
|
||||
|
||||
def update_owners(self):
|
||||
|
||||
"""Create an 'owner' object for each user and group instance"""
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Database model definitions for the 'users' app"""
|
||||
|
||||
import logging
|
||||
|
||||
@ -20,9 +20,7 @@ logger = logging.getLogger("inventree")
|
||||
|
||||
|
||||
class RuleSet(models.Model):
|
||||
"""
|
||||
A RuleSet is somewhat like a superset of the django permission class,
|
||||
in that in encapsulates a bunch of permissions.
|
||||
"""A RuleSet is somewhat like a superset of the django permission class, in that in encapsulates a bunch of permissions.
|
||||
|
||||
There are *many* apps models used within InvenTree,
|
||||
so it makes sense to group them into "roles".
|
||||
@ -192,6 +190,7 @@ class RuleSet(models.Model):
|
||||
]
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines additional model properties"""
|
||||
unique_together = (
|
||||
('name', 'group'),
|
||||
)
|
||||
@ -221,10 +220,7 @@ class RuleSet(models.Model):
|
||||
|
||||
@classmethod
|
||||
def check_table_permission(cls, user, table, permission):
|
||||
"""
|
||||
Check if the provided user has the specified permission against the table
|
||||
"""
|
||||
|
||||
"""Check if the provided user has the specified permission against the table."""
|
||||
# If the table does *not* require permissions
|
||||
if table in cls.RULESET_IGNORE:
|
||||
return True
|
||||
@ -255,11 +251,7 @@ class RuleSet(models.Model):
|
||||
|
||||
@staticmethod
|
||||
def get_model_permission_string(model, permission):
|
||||
"""
|
||||
Construct the correctly formatted permission string,
|
||||
given the app_model name, and the permission type.
|
||||
"""
|
||||
|
||||
"""Construct the correctly formatted permission string, given the app_model name, and the permission type."""
|
||||
model, app = split_model(model)
|
||||
|
||||
return "{app}.{perm}_{model}".format(
|
||||
@ -269,7 +261,7 @@ class RuleSet(models.Model):
|
||||
)
|
||||
|
||||
def __str__(self, debug=False): # pragma: no cover
|
||||
""" Ruleset string representation """
|
||||
"""Ruleset string representation."""
|
||||
if debug:
|
||||
# Makes debugging easier
|
||||
return f'{str(self.group).ljust(15)}: {self.name.title().ljust(15)} | ' \
|
||||
@ -279,10 +271,11 @@ class RuleSet(models.Model):
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Intercept the 'save' functionality to make addtional permission changes:
|
||||
|
||||
# It does not make sense to be able to change / create something,
|
||||
# but not be able to view it!
|
||||
|
||||
It does not make sense to be able to change / create something,
|
||||
but not be able to view it!
|
||||
"""
|
||||
if self.can_add or self.can_change or self.can_delete:
|
||||
self.can_view = True
|
||||
|
||||
@ -296,15 +289,12 @@ class RuleSet(models.Model):
|
||||
self.group.save()
|
||||
|
||||
def get_models(self):
|
||||
"""
|
||||
Return the database tables / models that this ruleset covers.
|
||||
"""
|
||||
|
||||
"""Return the database tables / models that this ruleset covers."""
|
||||
return self.RULESET_MODELS.get(self.name, [])
|
||||
|
||||
|
||||
def split_model(model):
|
||||
"""get modelname and app from modelstring"""
|
||||
"""Get modelname and app from modelstring."""
|
||||
*app, model = model.split('_')
|
||||
|
||||
# handle models that have
|
||||
@ -317,7 +307,7 @@ def split_model(model):
|
||||
|
||||
|
||||
def split_permission(app, perm):
|
||||
"""split permission string into permission and model"""
|
||||
"""Split permission string into permission and model."""
|
||||
permission_name, *model = perm.split('_')
|
||||
# handle models that have underscores
|
||||
if len(model) > 1: # pragma: no cover
|
||||
@ -328,10 +318,7 @@ def split_permission(app, perm):
|
||||
|
||||
|
||||
def update_group_roles(group, debug=False):
|
||||
"""
|
||||
|
||||
Iterates through all of the RuleSets associated with the group,
|
||||
and ensures that the correct permissions are either applied or removed from the group.
|
||||
"""Iterates through all of the RuleSets associated with the group, and ensures that the correct permissions are either applied or removed from the group.
|
||||
|
||||
This function is called under the following conditions:
|
||||
|
||||
@ -339,9 +326,7 @@ def update_group_roles(group, debug=False):
|
||||
b) Whenver the group object is updated
|
||||
|
||||
The RuleSet model has complete control over the permissions applied to any group.
|
||||
|
||||
"""
|
||||
|
||||
if not canAppAccessDatabase(allow_test=True):
|
||||
return # pragma: no cover
|
||||
|
||||
@ -367,15 +352,13 @@ def update_group_roles(group, debug=False):
|
||||
permissions_to_delete = set()
|
||||
|
||||
def add_model(name, action, allowed):
|
||||
"""
|
||||
Add a new model to the pile:
|
||||
"""Add a new model to the pile.
|
||||
|
||||
args:
|
||||
name - The name of the model e.g. part_part
|
||||
action - The permission action e.g. view
|
||||
allowed - Whether or not the action is allowed
|
||||
Args:
|
||||
name: The name of the model e.g. part_part
|
||||
action: The permission action e.g. view
|
||||
allowed: Whether or not the action is allowed
|
||||
"""
|
||||
|
||||
if action not in ['view', 'add', 'change', 'delete']: # pragma: no cover
|
||||
raise ValueError("Action {a} is invalid".format(a=action))
|
||||
|
||||
@ -418,16 +401,13 @@ def update_group_roles(group, debug=False):
|
||||
add_model(model, 'delete', ruleset.can_delete)
|
||||
|
||||
def get_permission_object(permission_string):
|
||||
"""
|
||||
Find the permission object in the database,
|
||||
from the simplified permission string
|
||||
"""Find the permission object in the database, from the simplified permission string.
|
||||
|
||||
Args:
|
||||
permission_string - a simplified permission_string e.g. 'part.view_partcategory'
|
||||
permission_string: a simplified permission_string e.g. 'part.view_partcategory'
|
||||
|
||||
Returns the permission object in the database associated with the permission string
|
||||
"""
|
||||
|
||||
(app, perm) = permission_string.split('.')
|
||||
|
||||
perm, model = split_permission(app, perm)
|
||||
@ -496,23 +476,18 @@ def update_group_roles(group, debug=False):
|
||||
|
||||
@receiver(post_save, sender=Group, dispatch_uid='create_missing_rule_sets')
|
||||
def create_missing_rule_sets(sender, instance, **kwargs):
|
||||
"""
|
||||
Called *after* a Group object is saved.
|
||||
As the linked RuleSet instances are saved *before* the Group,
|
||||
then we can now use these RuleSet values to update the
|
||||
group permissions.
|
||||
"""
|
||||
"""Called *after* a Group object is saved.
|
||||
|
||||
As the linked RuleSet instances are saved *before* the Group, then we can now use these RuleSet values to update the group permissions.
|
||||
"""
|
||||
update_group_roles(instance)
|
||||
|
||||
|
||||
def check_user_role(user, role, permission):
|
||||
"""
|
||||
Check if a user has a particular role:permission combination.
|
||||
"""Check if a user has a particular role:permission combination.
|
||||
|
||||
If the user is a superuser, this will return True
|
||||
"""
|
||||
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
@ -539,8 +514,8 @@ def check_user_role(user, role, permission):
|
||||
|
||||
|
||||
class Owner(models.Model):
|
||||
"""
|
||||
The Owner class is a proxy for a Group or User instance.
|
||||
"""The Owner class is a proxy for a Group or User instance.
|
||||
|
||||
Owner can be associated to any InvenTree model (part, stock, build, etc.)
|
||||
|
||||
owner_type: Model type (Group or User)
|
||||
@ -550,13 +525,12 @@ class Owner(models.Model):
|
||||
|
||||
@classmethod
|
||||
def get_owners_matching_user(cls, user):
|
||||
"""
|
||||
Return all "owner" objects matching the provided user:
|
||||
"""Return all "owner" objects matching the provided user.
|
||||
|
||||
A) An exact match for the user
|
||||
B) Any groups that the user is a part of
|
||||
Includes:
|
||||
- An exact match for the user
|
||||
- Any groups that the user is a part of
|
||||
"""
|
||||
|
||||
user_type = ContentType.objects.get(app_label='auth', model='user')
|
||||
group_type = ContentType.objects.get(app_label='auth', model='group')
|
||||
|
||||
@ -578,9 +552,11 @@ class Owner(models.Model):
|
||||
|
||||
@staticmethod
|
||||
def get_api_url(): # pragma: no cover
|
||||
"""Returns the API endpoint URL associated with the Owner model"""
|
||||
return reverse('api-owner-list')
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines extra model properties"""
|
||||
# Ensure all owners are unique
|
||||
constraints = [
|
||||
UniqueConstraint(fields=['owner_type', 'owner_id'],
|
||||
@ -594,25 +570,20 @@ class Owner(models.Model):
|
||||
owner = GenericForeignKey('owner_type', 'owner_id')
|
||||
|
||||
def __str__(self):
|
||||
""" Defines the owner string representation """
|
||||
"""Defines the owner string representation."""
|
||||
return f'{self.owner} ({self.owner_type.name})'
|
||||
|
||||
def name(self):
|
||||
"""
|
||||
Return the 'name' of this owner
|
||||
"""
|
||||
"""Return the 'name' of this owner."""
|
||||
return str(self.owner)
|
||||
|
||||
def label(self):
|
||||
"""
|
||||
Return the 'type' label of this owner i.e. 'user' or 'group'
|
||||
"""
|
||||
"""Return the 'type' label of this owner i.e. 'user' or 'group'."""
|
||||
return str(self.owner_type.name)
|
||||
|
||||
@classmethod
|
||||
def create(cls, obj):
|
||||
""" Check if owner exist then create new owner entry """
|
||||
|
||||
"""Check if owner exist then create new owner entry."""
|
||||
# Check for existing owner
|
||||
existing_owner = cls.get_owner(obj)
|
||||
|
||||
@ -627,8 +598,7 @@ class Owner(models.Model):
|
||||
|
||||
@classmethod
|
||||
def get_owner(cls, user_or_group):
|
||||
""" Get owner instance for a group or user """
|
||||
|
||||
"""Get owner instance for a group or user."""
|
||||
user_model = get_user_model()
|
||||
owner = None
|
||||
content_type_id = 0
|
||||
@ -651,11 +621,10 @@ class Owner(models.Model):
|
||||
return owner
|
||||
|
||||
def get_related_owners(self, include_group=False):
|
||||
"""
|
||||
Get all owners "related" to an owner.
|
||||
"""Get all owners "related" to an owner.
|
||||
|
||||
This method is useful to retrieve all "user-type" owners linked to a "group-type" owner
|
||||
"""
|
||||
|
||||
user_model = get_user_model()
|
||||
related_owners = None
|
||||
|
||||
@ -680,21 +649,13 @@ class Owner(models.Model):
|
||||
@receiver(post_save, sender=Group, dispatch_uid='create_owner')
|
||||
@receiver(post_save, sender=get_user_model(), dispatch_uid='create_owner')
|
||||
def create_owner(sender, instance, **kwargs):
|
||||
"""
|
||||
Callback function to create a new owner instance
|
||||
after either a new group or user instance is saved.
|
||||
"""
|
||||
|
||||
"""Callback function to create a new owner instance after either a new group or user instance is saved."""
|
||||
Owner.create(obj=instance)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Group, dispatch_uid='delete_owner')
|
||||
@receiver(post_delete, sender=get_user_model(), dispatch_uid='delete_owner')
|
||||
def delete_owner(sender, instance, **kwargs):
|
||||
"""
|
||||
Callback function to delete an owner instance
|
||||
after either a new group or user instance is deleted.
|
||||
"""
|
||||
|
||||
"""Callback function to delete an owner instance after either a new group or user instance is deleted."""
|
||||
owner = Owner.get_owner(instance)
|
||||
owner.delete()
|
||||
|
@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DRF API serializers for the 'users' app"""
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
@ -10,10 +10,10 @@ from .models import Owner
|
||||
|
||||
|
||||
class UserSerializer(InvenTreeModelSerializer):
|
||||
""" Serializer for a User
|
||||
"""
|
||||
"""Serializer for a User."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines serializer fields."""
|
||||
model = User
|
||||
fields = ('pk',
|
||||
'username',
|
||||
@ -23,15 +23,14 @@ class UserSerializer(InvenTreeModelSerializer):
|
||||
|
||||
|
||||
class OwnerSerializer(InvenTreeModelSerializer):
|
||||
"""
|
||||
Serializer for an "Owner" (either a "user" or a "group")
|
||||
"""
|
||||
"""Serializer for an "Owner" (either a "user" or a "group")"""
|
||||
|
||||
name = serializers.CharField(read_only=True)
|
||||
|
||||
label = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines serializer fields."""
|
||||
model = Owner
|
||||
fields = [
|
||||
'pk',
|
||||
|
@ -1,6 +1,4 @@
|
||||
"""
|
||||
Unit tests for the user model database migrations
|
||||
"""
|
||||
"""Unit tests for the user model database migrations."""
|
||||
|
||||
from django_test_migrations.contrib.unittest_case import MigratorTestCase
|
||||
|
||||
@ -8,15 +6,13 @@ from InvenTree import helpers
|
||||
|
||||
|
||||
class TestForwardMigrations(MigratorTestCase):
|
||||
"""
|
||||
Test entire schema migration sequence for the users app
|
||||
"""
|
||||
"""Test entire schema migration sequence for the users app."""
|
||||
|
||||
migrate_from = ('users', helpers.getOldestMigrationFile('users'))
|
||||
migrate_to = ('users', helpers.getNewestMigrationFile('users'))
|
||||
|
||||
def prepare(self):
|
||||
|
||||
"""Setup the initial state of the database before migrations"""
|
||||
User = self.old_state.apps.get_model('auth', 'user')
|
||||
|
||||
User.objects.create(
|
||||
@ -32,7 +28,7 @@ class TestForwardMigrations(MigratorTestCase):
|
||||
)
|
||||
|
||||
def test_users_exist(self):
|
||||
|
||||
"""Test that users exist in the database"""
|
||||
User = self.new_state.apps.get_model('auth', 'user')
|
||||
|
||||
self.assertEqual(User.objects.count(), 2)
|
||||
|
@ -1,3 +1,5 @@
|
||||
"""Unit tests for the 'users' app"""
|
||||
|
||||
from django.apps import apps
|
||||
from django.contrib.auth.models import Group
|
||||
from django.test import TestCase
|
||||
@ -10,12 +12,10 @@ from users.models import Owner, RuleSet
|
||||
|
||||
|
||||
class RuleSetModelTest(TestCase):
|
||||
"""
|
||||
Some simplistic tests to ensure the RuleSet model is setup correctly.
|
||||
"""
|
||||
"""Some simplistic tests to ensure the RuleSet model is setup correctly."""
|
||||
|
||||
def test_ruleset_models(self):
|
||||
|
||||
"""Test that the role rulesets work as intended"""
|
||||
keys = RuleSet.RULESET_MODELS.keys()
|
||||
|
||||
# Check if there are any rulesets which do not have models defined
|
||||
@ -48,11 +48,7 @@ class RuleSetModelTest(TestCase):
|
||||
self.assertEqual(len(empty), 0)
|
||||
|
||||
def test_model_names(self):
|
||||
"""
|
||||
Test that each model defined in the rulesets is valid,
|
||||
based on the database schema!
|
||||
"""
|
||||
|
||||
"""Test that each model defined in the rulesets is valid, based on the database schema!"""
|
||||
available_models = apps.get_models()
|
||||
|
||||
available_tables = set()
|
||||
@ -108,10 +104,7 @@ class RuleSetModelTest(TestCase):
|
||||
self.assertEqual(len(extra_models), 0)
|
||||
|
||||
def test_permission_assign(self):
|
||||
"""
|
||||
Test that the permission assigning works!
|
||||
"""
|
||||
|
||||
"""Test that the permission assigning works!"""
|
||||
# Create a new group
|
||||
group = Group.objects.create(name="Test group")
|
||||
|
||||
@ -161,17 +154,16 @@ class RuleSetModelTest(TestCase):
|
||||
|
||||
|
||||
class OwnerModelTest(InvenTreeTestCase):
|
||||
"""
|
||||
Some simplistic tests to ensure the Owner model is setup correctly.
|
||||
"""
|
||||
"""Some simplistic tests to ensure the Owner model is setup correctly."""
|
||||
|
||||
def do_request(self, endpoint, filters, status_code=200):
|
||||
"""Perform an API request"""
|
||||
response = self.client.get(endpoint, filters, format='json')
|
||||
self.assertEqual(response.status_code, status_code)
|
||||
return response.data
|
||||
|
||||
def test_owner(self):
|
||||
|
||||
"""Tests for the 'owner' model"""
|
||||
# Check that owner was created for user
|
||||
user_as_owner = Owner.get_owner(self.user)
|
||||
self.assertEqual(type(user_as_owner), Owner)
|
||||
@ -212,9 +204,7 @@ class OwnerModelTest(InvenTreeTestCase):
|
||||
self.assertEqual(group_as_owner, None)
|
||||
|
||||
def test_api(self):
|
||||
"""
|
||||
Test user APIs
|
||||
"""
|
||||
"""Test user APIs."""
|
||||
self.client.logout()
|
||||
|
||||
# not authed
|
||||
@ -231,9 +221,7 @@ class OwnerModelTest(InvenTreeTestCase):
|
||||
# self.do_request(reverse('api-owner-detail', kwargs={'pk': self.user.id}), {})
|
||||
|
||||
def test_token(self):
|
||||
"""
|
||||
Test token mechanisms
|
||||
"""
|
||||
"""Test token mechanisms."""
|
||||
self.client.logout()
|
||||
|
||||
token = Token.objects.filter(user=self.user)
|
||||
|
@ -1 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
Reference in New Issue
Block a user