def create(self, validated_data): """Override of create method.""" """ We have a bit of extra checking around this in order to provide descriptive messages when something goes wrong, but this method is essentially just: return ExampleModel.objects.create(**validated_data) If there are many to many fields present on the instance then they cannot be set until the model is instantiated, in which case the implementation is like so: example_relationship = validated_data.pop('example_relationship') instance = ExampleModel.objects.create(**validated_data) instance.example_relationship = example_relationship return instance The default implementation also does not handle nested relationships. If you want to support writable nested relationships you'll need to write an explicit `.create()` method. """ raise_errors_on_nested_writes("create", self, validated_data) ModelClass = self.Meta.model # Remove many-to-many relationships from validated_data. # They are not valid arguments to the default `.create()` method, # as they require that the instance has already been saved. info = model_meta.get_field_info(ModelClass) many_to_many = {} for field_name, relation_info in info.relations.items(): if relation_info.to_many and (field_name in validated_data): many_to_many[field_name] = validated_data.pop(field_name) try: for request_tenant in schema_handler( self.context["request"].tenant): instance = ModelClass._default_manager.create( **validated_data, tenant=request_tenant) except TypeError: tb = traceback.format_exc() msg = ("Got a `TypeError` when calling `%s.%s.create()`. " "This may be because you have a writable field on the " "serializer class that is not a valid argument to " "`%s.%s.create()`. You may need to make the field " "read-only, or override the %s.create() method to handle " "this correctly.\nOriginal exception was:\n %s" % ( ModelClass.__name__, ModelClass._default_manager.name, ModelClass.__name__, ModelClass._default_manager.name, self.__class__.__name__, tb, )) raise TypeError(msg) # Save many-to-many relationships after the instance is created. if many_to_many: for field_name, value in many_to_many.items(): field = getattr(instance, field_name) field.set(value) return instance
def remove_roles(group, role_ids, tenant): """Process list of roles and remove them from the group.""" roles = Role.objects.filter(uuid__in=role_ids) role_names = list(roles.values_list("name", flat=True)) for tenant_schema in schema_handler(tenant): group = Group.objects.get(name=group.name, tenant=tenant) roles = Role.objects.filter(name__in=role_names, tenant=tenant) for policy in group.policies.all(): policy.roles.remove(*roles)
def update(self, instance, validated_data): """Patch the role object.""" tenant = self.context["request"].tenant role_name = instance.name update_data = validate_role_update(instance, validated_data) for tenant_schema in schema_handler(tenant): instance = update_role(role_name, update_data, tenant, clear_access=False) return instance
def update(self, instance, validated_data): """Update the role object in the database.""" access_list = validated_data.pop("access") tenant = self.context["request"].tenant role_name = instance.name update_data = validate_role_update(instance, validated_data) for tenant_schema in schema_handler(tenant): instance = update_role(role_name, update_data, tenant) create_access_for_role(instance, access_list, tenant) return instance
def create(self, validated_data): """Create the role object in the database.""" name = validated_data.pop("name") display_name = validated_data.pop("display_name", name) description = validated_data.pop("description", None) access_list = validated_data.pop("access") tenant = self.context["request"].tenant for tenant_schema in schema_handler(tenant): role = Role.objects.create(name=name, description=description, display_name=display_name, tenant=tenant) create_access_for_role(role, access_list, tenant) return role
def add_roles(group, roles_or_role_ids, tenant, user=None, replace=False, duplicate_in_public=False): """Process list of roles and add them to the group.""" if not isinstance(roles_or_role_ids, QuerySet): # If given an iterable of UUIDs, get the corresponding objects roles = Role.objects.filter(uuid__in=roles_or_role_ids) else: roles = roles_or_role_ids group_name = group.name role_names = list(roles.values_list("name", flat=True)) for tenant_schema in schema_handler(tenant, include_public=duplicate_in_public): group, created = Group.objects.get_or_create(name=group_name, tenant=tenant) system_policy_name = "System Policy for Group {}".format(group.uuid) system_policy, system_policy_created = Policy.objects.update_or_create( system=True, group=group, name=system_policy_name, defaults={"tenant": tenant}) if system_policy_created: logger.info( f"Created new system policy for tenant {tenant_schema.schema_name}." ) else: if replace: system_policy.roles.clear() roles = Role.objects.filter( Q(tenant=tenant) | Q(tenant=Tenant.objects.get(schema_name="public")), name__in=role_names) for role in roles: accesses = role.access.all() for access in accesses: if access.permission_application( ) == "rbac" and user and not user.admin: key = "add-roles" message = f"Non-admin users cannot add RBAC role {role.display_name} to groups." raise serializers.ValidationError({key: _(message)}) system_policy.roles.add(role)