Hi there,

Long-time lurker / Django user; first-time poster.

Ask: Any other ideas for ensuring that ForeignKey relationships are valid?

Context / background: I'm working on a multitenant app, and looking at ways 
of ensuring the integrity of the data in the system. Specifically, I want 
to ensure that for a "tenant'ed" model that contains ForeignKey fields, 
those FK relations match the same tenant.

Unfortunately CHECK constraints aren't suitable, as these are limited to 
just the row being inserted/updated, so nested SELECTs or similar at the 
database level aren't possible. It seems as if a TRIGGER could do the job, 
but I'm wary of going down this path if there are other solutions that I've 
missed.

At present I've implemented something along the following simplified lines 
within the application.

All ideas greatly appreciated. Thanks in advance :)

# models.py
class BaseModel(models.Model):
    match_fields: List[List[str]] = []

    class Meta:
        abstract = True

    def clean(self):
        self.validate_matching_fields()
        return super().clean()

    def validate_matching_fields(self) -> None:
        for field_names in self.match_fields:
            field_values: Dict[str, Any] = dict()
            for field_name in field_names:
                value = self
                for field in field_name.split("."):
                    value = getattr(value, field)
                field_values[field_name] = value
            _values = list(field_values.values())
            assert len(_values) > 1
            values_equal = all([V == _values[0] for V in _values])
            if not values_equal:  # pragma: no branch
                msg = f"One or more required fields not matching: 
{field_values}."
                raise ValidationError(msg)
        return

# Tenant model
class Organization(models.Model):
    name = models.CharField(max_length=64)

class Author(models.Model):
    organization = models.ForeignKey(to=Organization, 
on_delete=models.CASCADE)
    name = models.CharField(max_length=64)

# Target model
# I want to ensure that BlogPost.organization == 
BlogPost.lead_author.organization
class BlogPost(BaseModel):
    match_fields = [["organization.id", "lead_author.organization.id"]]

    organization = models.ForeignKey(to=Organization, 
on_delete=models.CASCADE)
    lead_author = models.ForeignKey(to=Author, on_delete=models.CASCADE)
    slug = models.SlugField(max_length=64)

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/18fe8095-3f46-44e6-b418-49e6a411667cn%40googlegroups.com.

Reply via email to