Skip to content

Commit

Permalink
Update UniqueConstraints. Update serializer. Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
raphodn committed Sep 14, 2024
1 parent 7c0dc23 commit 9f6ec75
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 11 deletions.
2 changes: 1 addition & 1 deletion open_prices/api/locations/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ class LocationFilter(django_filters.FilterSet):

class Meta:
model = Location
fields = ["price_count"]
fields = ["type", "price_count"]
12 changes: 12 additions & 0 deletions open_prices/api/locations/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,15 @@ class LocationCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Location
fields = Location.CREATE_FIELDS

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# with UniqueConstraints, DRF sets some fields as required
for field in (
Location.TYPE_OSM_MANDATORY_FIELDS + Location.TYPE_ONLINE_MANDATORY_FIELDS
):
self.fields[field].required = False

# with UniqueConstraints, DRF wrongly validates.
# Leave it to the model's save()
validators = []
28 changes: 25 additions & 3 deletions open_prices/api/locations/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"osm_name": "Auchan",
"price_count": 50,
}
LOCATION_ONLINE_DECATHLON = {
"type": location_constants.TYPE_ONLINE,
"website_url": "https://www.decathlon.fr/",
"price_count": 15,
}


class LocationListApiTest(TestCase):
Expand Down Expand Up @@ -68,18 +73,27 @@ def setUpTestData(cls):
LocationFactory(**LOCATION_OSM_NODE_652825274)
LocationFactory(**LOCATION_OSM_NODE_6509705997)
LocationFactory(**LOCATION_OSM_WAY_872934393)
LocationFactory(**LOCATION_ONLINE_DECATHLON)

def test_location_list_filter_by_osm_name(self):
url = self.url + "?osm_name__like=monop"
response = self.client.get(url)
self.assertEqual(response.data["total"], 1)
self.assertEqual(response.data["items"][0]["osm_name"], "Monoprix")

def test_location_list_filter_by_type(self):
url = self.url + "?type=ONLINE"
response = self.client.get(url)
self.assertEqual(response.data["total"], 1)
self.assertEqual(
response.data["items"][0]["type"], location_constants.TYPE_ONLINE
)

def test_location_list_filter_by_price_count(self):
# exact price_count
url = self.url + "?price_count=15"
response = self.client.get(url)
self.assertEqual(response.data["total"], 1)
self.assertEqual(response.data["total"], 1 + 1)
self.assertEqual(response.data["items"][0]["price_count"], 15)
# lte / gte
url = self.url + "?price_count__gte=20"
Expand All @@ -88,7 +102,7 @@ def test_location_list_filter_by_price_count(self):
self.assertEqual(response.data["items"][0]["price_count"], 50)
url = self.url + "?price_count__lte=20"
response = self.client.get(url)
self.assertEqual(response.data["total"], 2)
self.assertEqual(response.data["total"], 3)
self.assertEqual(response.data["items"][0]["price_count"], 15)


Expand Down Expand Up @@ -135,7 +149,7 @@ class LocationCreateApiTest(TestCase):
def setUpTestData(cls):
cls.url = reverse("api:locations-list")

def test_location_create(self):
def test_location_create_osm(self):
response = self.client.post(
self.url, LOCATION_OSM_NODE_652825274, content_type="application/json"
)
Expand All @@ -147,6 +161,14 @@ def test_location_create(self):
) # ignored (and post_save signal disabled)
self.assertEqual(response.data["price_count"], 0) # ignored

def test_location_create_online(self):
response = self.client.post(
self.url, LOCATION_ONLINE_DECATHLON, content_type="application/json"
)
self.assertEqual(response.status_code, 201)
self.assertEqual(response.data["website_url"], "https://www.decathlon.fr/")
self.assertEqual(response.data["price_count"], 0) # ignored


class LocationUpdateApiTest(TestCase):
@classmethod
Expand Down
6 changes: 3 additions & 3 deletions open_prices/locations/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

WEBSITE_URL_OK_LIST = [
"https://www.decathlon.fr/",
"https://www.decathlon.fr",
"www.decathlon.fr/",
"www.decathlon.fr",
"https://www.alltricks.fr",
"www.ekosport.fr/",
"www.auvieuxcampeur.fr",
]
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,24 @@ class Migration(migrations.Migration):
null=True,
),
),
migrations.AlterUniqueTogether(
name="location",
unique_together=set(),
),
migrations.AddConstraint(
model_name="location",
constraint=models.UniqueConstraint(
condition=models.Q(("type", "OSM")),
fields=("osm_id", "osm_type"),
name="unique_osm_constraint",
),
),
migrations.AddConstraint(
model_name="location",
constraint=models.UniqueConstraint(
condition=models.Q(("type", "ONLINE")),
fields=("website_url",),
name="unique_online_constraint",
),
),
]
15 changes: 13 additions & 2 deletions open_prices/locations/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.conf import settings
from django.core.validators import ValidationError
from django.db import models
from django.db.models import Count, signals
from django.db.models import Count, Q, UniqueConstraint, signals
from django.dispatch import receiver
from django.utils import timezone
from django_q.tasks import async_task
Expand Down Expand Up @@ -76,7 +76,18 @@ class Location(models.Model):
class Meta:
# managed = False
db_table = "locations"
unique_together = ["osm_id", "osm_type"]
constraints = [
UniqueConstraint(
name="unique_osm_constraint",
fields=["osm_id", "osm_type"],
condition=Q(type=location_constants.TYPE_OSM),
),
UniqueConstraint(
name="unique_online_constraint",
fields=["website_url"],
condition=Q(type=location_constants.TYPE_ONLINE),
),
]
verbose_name = "Location"
verbose_name_plural = "Locations"

Expand Down
11 changes: 9 additions & 2 deletions open_prices/locations/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ def test_location_osm_id_type_validation(self):
osm_id=6509705997,
osm_type=LOCATION_OSM_TYPE_NOT_OK,
)
# must be unique
# unique constraint
self.assertRaises(
ValidationError,
LocationFactory,
type=location_constants.TYPE_OSM,
osm_id=6509705997,
osm_type=location_constants.OSM_TYPE_NODE,
osm_type=location_constants.OSM_TYPE_OK_LIST[0],
)

def test_location_decimal_truncate_on_create(self):
Expand All @@ -97,6 +97,13 @@ def test_location_online_validation(self):
LocationFactory(
type=location_constants.TYPE_ONLINE, website_url=WEBSITE_URL
)
# unique constraint
self.assertRaises(
ValidationError,
LocationFactory,
type=location_constants.TYPE_ONLINE,
website_url=location_constants.WEBSITE_URL_OK_LIST[0],
)


class LocationQuerySetTest(TestCase):
Expand Down

0 comments on commit 9f6ec75

Please sign in to comment.