diff --git a/config/settings/base.py b/config/settings/base.py index 202ca33..3a71e7a 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -70,6 +70,8 @@ "rest_framework.authtoken", "corsheaders", "drf_spectacular", + "location_field", + "polymorphic", ] LOCAL_APPS = [ @@ -335,3 +337,8 @@ {"url": "https://akarpov.ru", "description": "Production server"}, ], } + +LOCATION_FIELD = { + "map.provider": "openstreetmap", + "search.provider": "nominatim", +} diff --git a/passfinder/events/migrations/0001_initial.py b/passfinder/events/migrations/0001_initial.py new file mode 100644 index 0000000..b64f8a6 --- /dev/null +++ b/passfinder/events/migrations/0001_initial.py @@ -0,0 +1,380 @@ +# Generated by Django 4.2.1 on 2023-05-19 17:14 + +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion +import location_field.models.plain + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("contenttypes", "0002_remove_content_type_name"), + ] + + operations = [ + migrations.CreateModel( + name="BasePoint", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("title", models.CharField(max_length=250)), + ("parser_source", models.CharField(max_length=250)), + ("description", models.TextField()), + ("creator", models.CharField(max_length=250)), + ("partner", models.CharField(max_length=250)), + ("payment_method", models.CharField(max_length=250)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="City", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("title", models.CharField(max_length=250)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Country", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("title", models.CharField(max_length=250)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Region", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("title", models.CharField(max_length=250)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Tag", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("name", models.CharField(max_length=250)), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Event", + fields=[ + ( + "basepoint_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="events.basepoint", + ), + ), + ("ya_id", models.CharField(blank=True, max_length=24)), + ( + "age", + models.CharField( + max_length=50, verbose_name="Возрастное ограничение" + ), + ), + ("booking_link", models.URLField()), + ("discover_moscow_link", models.URLField()), + ("duration", models.IntegerField(blank=True, null=True)), + ("ticket_price", models.IntegerField(blank=True, null=True)), + ("schedule", models.JSONField(null=True)), + ], + options={ + "abstract": False, + }, + bases=("events.basepoint",), + ), + migrations.CreateModel( + name="Excursion", + fields=[ + ( + "basepoint_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="events.basepoint", + ), + ), + ("duration_hours", models.IntegerField()), + ("price", models.IntegerField()), + ("minGroupCount", models.CharField(max_length=250)), + ("program", models.TextField()), + ], + options={ + "abstract": False, + }, + bases=("events.basepoint",), + ), + migrations.CreateModel( + name="Hotel", + fields=[ + ( + "basepoint_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="events.basepoint", + ), + ), + ("address", models.CharField(max_length=250)), + ( + "location", + location_field.models.plain.PlainLocationField(max_length=63), + ), + ("rooms", models.JSONField(null=True)), + ("email", models.CharField(max_length=250)), + ("stars", models.IntegerField(null=True)), + ], + options={ + "abstract": False, + }, + bases=("events.basepoint",), + ), + migrations.CreateModel( + name="Museum", + fields=[ + ( + "basepoint_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="events.basepoint", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("events.basepoint",), + ), + migrations.CreateModel( + name="Restaurant", + fields=[ + ( + "basepoint_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="events.basepoint", + ), + ), + ("bill", models.IntegerField()), + ("avg_time_visit", models.IntegerField()), + ("can_reserve", models.BooleanField()), + ("working_time", models.JSONField(null=True)), + ( + "location", + location_field.models.plain.PlainLocationField(max_length=63), + ), + ("phone", models.CharField(max_length=18)), + ], + options={ + "abstract": False, + }, + bases=("events.basepoint",), + ), + migrations.CreateModel( + name="PointMedia", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("file", models.FileField(upload_to="uploads/")), + ("type", models.CharField(max_length=200)), + ( + "point", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="media", + to="events.basepoint", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="Place", + fields=[ + ( + "oid", + models.CharField(max_length=24, primary_key=True, serialize=False), + ), + ("address", models.CharField(max_length=250)), + ("parser_source", models.CharField(max_length=250)), + ("description", models.TextField()), + ( + "location", + location_field.models.plain.PlainLocationField(max_length=63), + ), + ( + "sites", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=250), size=None + ), + ), + ( + "phones", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=250), size=None + ), + ), + ("working_time", models.JSONField(null=True)), + ( + "city", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="places", + to="events.city", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.AddField( + model_name="city", + name="region", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="cities", + to="events.region", + ), + ), + migrations.AddField( + model_name="basepoint", + name="city", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="points", + to="events.city", + ), + ), + migrations.AddField( + model_name="basepoint", + name="polymorphic_ctype", + field=models.ForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="polymorphic_%(app_label)s.%(class)s_set+", + to="contenttypes.contenttype", + ), + ), + migrations.AddField( + model_name="basepoint", + name="region", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="points", + to="events.region", + ), + ), + migrations.CreateModel( + name="HotelPhone", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=50)), + ("number", models.CharField(max_length=18)), + ( + "hotel", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="phones", + to="events.hotel", + ), + ), + ], + ), + migrations.CreateModel( + name="ExcursionRoute", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("active", models.BooleanField(default=True)), + ( + "excursion", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="routes", + to="events.excursion", + ), + ), + ( + "point", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="routes", + to="events.event", + ), + ), + ], + ), + ] diff --git a/passfinder/events/models.py b/passfinder/events/models.py index 30afeeb..1d167d2 100644 --- a/passfinder/events/models.py +++ b/passfinder/events/models.py @@ -1,4 +1,6 @@ +from django.contrib.postgres.fields import ArrayField from django.db import models +from location_field.models.plain import PlainLocationField from polymorphic.models import PolymorphicModel @@ -12,10 +14,16 @@ class Meta: class Region(OIDModel): title = models.CharField(max_length=250) + def __str__(self): + return self.title + class Country(OIDModel): title = models.CharField(max_length=250) + def __str__(self): + return self.title + class City(OIDModel): title = models.CharField(max_length=250) @@ -23,16 +31,36 @@ class City(OIDModel): "Region", related_name="cities", null=True, on_delete=models.SET_NULL ) + def __str__(self): + return self.title + + +class Place(OIDModel): + address = models.CharField(max_length=250) + parser_source = models.CharField(max_length=250) + city = models.ForeignKey( + "City", related_name="places", null=True, on_delete=models.SET_NULL + ) + description = models.TextField() + location = PlainLocationField(zoom=6) + sites = ArrayField(models.CharField(max_length=250)) + phones = ArrayField(models.CharField(max_length=250)) + working_time = models.JSONField(null=True) + class Tag(OIDModel): name = models.CharField(max_length=250) + def __str__(self): + return self.name -class BasePoints(OIDModel, PolymorphicModel): + +class BasePoint(OIDModel, PolymorphicModel): title = models.CharField(max_length=250) + parser_source = models.CharField(max_length=250) description = models.TextField() city = models.ForeignKey( - "City", related_name="places", null=True, on_delete=models.SET_NULL + "City", related_name="points", null=True, on_delete=models.SET_NULL ) region = models.ForeignKey( "Region", related_name="points", null=True, on_delete=models.SET_NULL @@ -40,4 +68,68 @@ class BasePoints(OIDModel, PolymorphicModel): creator = models.CharField(max_length=250) partner = models.CharField(max_length=250) payment_method = models.CharField(max_length=250) - tags = models.ManyToManyField("Tag", related_name="points") + # tags = models.ManyToManyField("Tag", related_name="points") + + def __str__(self): + return self.title + + +class PointMedia(OIDModel): + file = models.FileField(upload_to="uploads/") + type = models.CharField(max_length=200) + point = models.ForeignKey( + "BasePoint", related_name="media", on_delete=models.CASCADE + ) + + +class Event(BasePoint): + ya_id = models.CharField(blank=True, max_length=24) + age = models.CharField("Возрастное ограничение", max_length=50) + booking_link = models.URLField() + discover_moscow_link = models.URLField() + duration = models.IntegerField(blank=True, null=True) + ticket_price = models.IntegerField(blank=True, null=True) + schedule = models.JSONField(null=True) + + +class Hotel(BasePoint): + address = models.CharField(max_length=250) + location = PlainLocationField(zoom=6) + rooms = models.JSONField(null=True) + email = models.CharField(max_length=250) + stars = models.IntegerField(null=True) + + +class HotelPhone(models.Model): + hotel = models.ForeignKey("Hotel", related_name="phones", on_delete=models.CASCADE) + name = models.CharField(max_length=50) + number = models.CharField(max_length=18) + + +class Museum(BasePoint): + # TODO + ... + + +class Excursion(BasePoint): + duration_hours = models.IntegerField() + price = models.IntegerField() + minGroupCount = models.CharField(max_length=250) + program = models.TextField() + + +class ExcursionRoute(models.Model): + excursion = models.ForeignKey( + "Excursion", related_name="routes", on_delete=models.CASCADE + ) + point = models.ForeignKey("Event", related_name="routes", on_delete=models.CASCADE) + active = models.BooleanField(default=True) + + +class Restaurant(BasePoint): + bill = models.IntegerField() + avg_time_visit = models.IntegerField() + can_reserve = models.BooleanField() + working_time = models.JSONField(null=True) + location = PlainLocationField(zoom=6) + phone = models.CharField(max_length=18)