Skip to content

Commit

Permalink
Fixes #103 -- support natural keys for the models
Browse files Browse the repository at this point in the history
Added support for natural keys for the cookie and cookie group
models. This required adding another unique constraint, this
time to the cookie model. A specific cookie within a group was
already looked up by name and domain in the existing code, so
this DB constraint confirms the usage. Duplicate data would not
have yielded the expected results, but users may not be aware
of it, so this is also a breaking change.

You can now dump and load fixtures for cookie consent, using
these natural keys and avoid database primary key conflicts.
  • Loading branch information
sergei-maertens committed May 9, 2024
1 parent b24a095 commit 13aa8c3
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 5 deletions.
19 changes: 19 additions & 0 deletions cookie_consent/migrations/0004_cookie_natural_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.13 on 2024-05-09 20:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("cookie_consent", "0003_alter_cookiegroup_varname"),
]

operations = [
migrations.AddConstraint(
model_name="cookie",
constraint=models.UniqueConstraint(
fields=("cookiegroup", "name", "domain"), name="natural_key"
),
),
]
29 changes: 27 additions & 2 deletions cookie_consent/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ def update(self, **kwargs):
return super().update(**kwargs)


class CookieGroupManager(models.Manager.from_queryset(BaseQueryset)):
def get_by_natural_key(self, varname):
return self.get(varname=varname)


class CookieGroup(models.Model):
varname = models.CharField(
_("Variable name"),
Expand All @@ -70,7 +75,7 @@ class CookieGroup(models.Model):
ordering = models.IntegerField(_("Ordering"), default=0)
created = models.DateTimeField(_("Created"), auto_now_add=True, blank=True)

objects = BaseQueryset.as_manager()
objects = CookieGroupManager()

class Meta:
verbose_name = _("Cookie Group")
Expand All @@ -88,6 +93,9 @@ def save(self, *args, **kwargs):
def delete(self, *args, **kwargs):
return super().delete(*args, **kwargs)

def natural_key(self):
return (self.varname,)

def get_version(self) -> str:
try:
return str(self.cookie_set.all()[0].get_version())
Expand All @@ -104,6 +112,12 @@ def for_json(self) -> CookieGroupDict:
}


class CookieManager(models.Manager.from_queryset(BaseQueryset)):
def get_by_natural_key(self, name, domain, cookiegroup):
group = CookieGroup.objects.get_by_natural_key(cookiegroup)
return self.get(cookiegroup=group, name=name, domain=domain)


class Cookie(models.Model):
cookiegroup = models.ForeignKey(
CookieGroup,
Expand All @@ -116,11 +130,17 @@ class Cookie(models.Model):
domain = models.CharField(_("Domain"), max_length=250, blank=True)
created = models.DateTimeField(_("Created"), auto_now_add=True, blank=True)

objects = BaseQueryset.as_manager()
objects = CookieManager()

class Meta:
verbose_name = _("Cookie")
verbose_name_plural = _("Cookies")
constraints = [
models.UniqueConstraint(
fields=("cookiegroup", "name", "domain"),
name="natural_key",
),
]
ordering = ["-created"]

def __str__(self):
Expand All @@ -134,6 +154,11 @@ def save(self, *args, **kwargs):
def delete(self, *args, **kwargs):
return super().delete(*args, **kwargs)

def natural_key(self):
return (self.name, self.domain) + self.cookiegroup.natural_key()

natural_key.dependencies = ["cookie_consent.cookiegroup"]

@property
def varname(self):
return "%s=%s:%s" % (self.cookiegroup.varname, self.name, self.domain)
Expand Down
5 changes: 3 additions & 2 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ Changelog
------------------

💥 This feature release has a potential breaking change. The ``CookieGroup.varname``
field now has a unique constraint on it. If you have duplicate values, this migration
will crash.
field now has a unique constraint on it. The ``Cookie`` model now has a unique
constraint on ``cookiegroup``, ``name`` and ``domain``. If you have duplicate values,
this migration will crash.

* ...

Expand Down
19 changes: 19 additions & 0 deletions tests/test_cookie_group_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

from cookie_consent.models import CookieGroup


def test_natural_key():
group = CookieGroup(varname="social")

assert group.natural_key() == ("social",)


@pytest.mark.django_db
def test_load_by_natural_key():
social_group = CookieGroup.objects.create(varname="social")
CookieGroup.objects.create(varname="other")

loaded_group = CookieGroup.objects.get_by_natural_key("social")

assert loaded_group == social_group
24 changes: 24 additions & 0 deletions tests/test_cookie_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest

from cookie_consent.models import Cookie, CookieGroup


def test_natural_key():
cookie = Cookie(
cookiegroup=CookieGroup(varname="analytics"), name="trck", domain="example.com"
)

assert cookie.natural_key() == ("trck", "example.com", "analytics")


@pytest.mark.django_db
def test_load_by_natural_key():
social_group = CookieGroup.objects.create(varname="social")
cookie = Cookie.objects.create(
cookiegroup=social_group, name="trck", domain="example.com"
)
Cookie.objects.create(cookiegroup=social_group, name="other", domain="example.com")

loaded_cookie = Cookie.objects.get_by_natural_key("trck", "example.com", "social")

assert loaded_cookie == cookie
1 change: 0 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from copy import deepcopy
from unittest import mock

from django.conf import settings
from django.core.cache import caches
Expand Down

0 comments on commit 13aa8c3

Please sign in to comment.