Compare commits

...

14 commits

Author SHA1 Message Date
058a64b4a9 put and post coords 2026-06-21 16:57:02 -04:00
92db8d61ed leaflet 2026-06-21 14:11:51 -04:00
7fcca38986 updating postgres to postgis 2026-06-21 14:02:59 -04:00
bc46ca831c category and tag functionality 2026-06-20 22:26:23 -04:00
175ffda130 added cors headers to settings file, and changed request.post to json.loads(request.body) for frontend (#2)
I updated the

- settings.py file to influce cors headers
- requirements.txt to include cors-headers
- Updated views/events, categories, and tags "request.post" to "json.loads(request.body)"
Please approve.

Co-authored-by: Madeleine <madeleine@Madeleines-MacBook-Air.local>
Reviewed-on: #2
Reviewed-by: dominic <me@domdit.com>
2026-06-20 17:59:53 -04:00
5448fd9d3c adding tag and category 2026-06-20 18:00:06 -04:00
29da461a53 updating DB 2026-06-20 17:17:40 -04:00
c36e52d4fc classy 2026-06-13 22:41:28 -04:00
72a29a6b8b classy 2026-06-13 22:39:23 -04:00
af65d4b57e manage 2026-06-13 22:03:56 -04:00
a061837802 sql 2026-06-13 22:01:37 -04:00
56d0c52d42 api 2026-06-13 22:00:19 -04:00
fbd2b76e0d add gitignore 2026-06-13 21:58:57 -04:00
04d947b288 updated README file (#1)
I updated README.md. Please approve it. Love u.

Co-authored-by: Madeleine <madeleine@Madeleines-MacBook-Air.local>
Reviewed-on: #1
Reviewed-by: dominic <me@domdit.com>
2026-06-13 16:03:46 -04:00
52 changed files with 823 additions and 50 deletions

217
.gitignore vendored Normal file
View file

@ -0,0 +1,217 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[codz]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
# Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
# poetry.lock
# poetry.toml
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
# pdm.lock
# pdm.toml
.pdm-python
.pdm-build/
# pixi
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
# pixi.lock
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
# in the .venv directory. It is recommended not to include this directory in version control.
.pixi
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# Redis
*.rdb
*.aof
*.pid
# RabbitMQ
mnesia/
rabbitmq/
rabbitmq-data/
# ActiveMQ
activemq-data/
# SageMath parsed files
*.sage.py
# Environments
.env
.envrc
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
# .idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Marimo
marimo/_static/
marimo/_lsp/
__marimo__/
# Streamlit
.streamlit/secrets.toml
config*

View file

@ -1,13 +1,18 @@
# Summary
A local app for local people.
A community-driven "Things To Do Near Me" platform that combines features of interactive maps, events, and local social recommendations.
Discover and share local events, hidden gems, activities, and experiences on an interactive map.
# Detailed Design
User should be able to create an event, search for events (MVPs), and share events (future).
Two users groups: Users who create events, and users who search for events. Users can be in both groups.
Interactive map with pins for events, and a list view of events.
Users can filter events by radius, category, tag, date, and location, etc.
## Functionality
### User Creates an Event
- Fills out form
- form includes:
- Form includes:
- Event Address
- Event Name
- Event Description
@ -44,11 +49,21 @@
## User Flow
## Web Pages
1. An interactive map page with pins for events, and a list view of events.
2. An event creation page with a form for users to fill out.
3. An event detail page that shows more information about the event.
4. An event editing page that allows users to edit or cancel their events.
5. An introduction page that explains the app and its features.
## DB Structure
## API Endpoints
## Authentication
Unique link for each event that can be used for editing and cancelling.
Future step:
- User accounts with email and password, or social login (Google, Facebook, etc.)
# Dependencies
- Frontend: React, Leaflet for interactive maps, and a UI library like Material-UI or Tailwind CSS.
- Backend: Django for API development, SQLite for database management, and like AWS S3 for image storage.

Binary file not shown.

View file

@ -1,3 +0,0 @@
from django.db import models
# Create your models here.

View file

@ -0,0 +1,10 @@
from rest_framework import serializers
from web.models.category import Category
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'

49
api/serializers/event.py Normal file
View file

@ -0,0 +1,49 @@
from rest_framework import serializers
from django.forms.models import model_to_dict
from web.models.event import Event
from web.models.event_category import EventCategory
from web.models.event_tag import EventTag
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = '__all__'
def to_representation(self, instance):
representation = super().to_representation(instance)
event_categories = EventCategory.objects.filter(event=instance).filter(active=True).all()
event_tags = EventTag.objects.filter(event=instance).filter(active=True).all()
representation['location'] = {
'lng': instance.coordinates.x if instance.coordinates else None,
'lat': instance.coordinates.y if instance.coordinates else None
}
representation['categories'] = self._parse_categories(event_categories)
representation['tags'] = self._parse_tags(event_tags)
return representation
def _parse_categories(self, event_categories):
categories = []
if event_categories.exists():
for event_category in event_categories:
categories.append({
'id': event_category.category.id,
'name': event_category.category.name,
'description': event_category.category.description,
'_meta': model_to_dict(event_category)
})
return categories
def _parse_tags(self, event_tags):
tags = []
if event_tags.exists():
for event_tag in event_tags:
tags.append({
'id': event_tag.tag.id,
'name': event_tag.tag.name,
'_meta': model_to_dict(event_tag)
})
return tags

View file

@ -0,0 +1,10 @@
from rest_framework import serializers
from web.models.event_category import EventCategory
class EventCategorySerializer(serializers.ModelSerializer):
class Meta:
model = EventCategory
fields = "all"

View file

@ -0,0 +1,10 @@
from rest_framework import serializers
from web.models.event_tag import EventTag
class EventTagSerializer(serializers.ModelSerializer):
class Meta:
model = EventTag
fields = "all"

12
api/serializers/tag.py Normal file
View file

@ -0,0 +1,12 @@
from rest_framework import serializers
from web.models.tag import Tag
class TagSerializer(serializers.ModelSerializer):
# to_representation(self, instance) if needed
# fk = Serializer()
class Meta:
model = Tag
fields = '__all__'

0
api/services/event.py Normal file
View file

View file

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.

29
api/views/base.py Normal file
View file

@ -0,0 +1,29 @@
from rest_framework.views import APIView
from rest_framework.response import Response
class BaseView(APIView):
SERIALIZER = None
def _build_response(self, data):
model_serializer = self.SERIALIZER(data)
response = Response()
response.data = model_serializer.data
return response
def _build_multi_response(self, data):
serialized_data = []
for d in data:
serializer = self.SERIALIZER(d)
serialized_data.append(serializer.data)
response = Response()
response.data = serialized_data
return response
def error_response(self, status_code, description):
response = Response()
response.status_code = status_code
response.data = {"error": description}
return response

44
api/views/category.py Normal file
View file

@ -0,0 +1,44 @@
import json
from django.shortcuts import get_object_or_404
from api.serializers.category import CategorySerializer
from api.views.base import BaseView
from web.models.category import Category
class CategoryView(BaseView):
SERIALIZER = CategorySerializer
def get(self, request, category_id=None):
if category_id:
category = get_object_or_404(Category, pk=category_id)
return self._build_response(category)
else:
categories = Category.objects.filter(active=True).all()
return self._build_multi_response(categories)
def post(self, request):
data = json.loads(request.body)
category = Category(
name=data.get('name'),
description=data.get('description')
)
category.save()
return self._build_response(category)
def put(self, request, category_id):
data = json.loads(request.body)
category = get_object_or_404(Category, pk=category_id)
category.name = data.get('name', category.name)
category.description = data.get('description', category.description)
return self._build_response(category)
def delete(self, request, category_id):
category = get_object_or_404(Category, pk=category_id)
category.active = False
category.save()
return self._build_response(category)

186
api/views/event.py Normal file
View file

@ -0,0 +1,186 @@
import json
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
from django.db import transaction
from django.shortcuts import get_object_or_404
from api.serializers.event import EventSerializer
from api.views.base import BaseView
from web.models.category import Category
from web.models.event_category import EventCategory
from web.models.event import Event
from web.models.tag import Tag
from web.models.event_tag import EventTag
class EventView(BaseView):
SERIALIZER = EventSerializer
def get(self, request, event_id=None):
if event_id:
event = get_object_or_404(Event, pk=event_id, active=True)
return self._build_response(event)
else:
return self._get_events_by_radius(request)
def _get_events_by_radius(self, request):
lat = request.GET.get('lat')
lng = request.GET.get('lng')
r = request.GET.get('r')
if all([lat, lng, r]):
try:
user_location = Point(float(lng), float(lat), srid=4326)
events = Event.objects.filter(active=True).filter(coordinates__dwithin=(user_location, D(mi=float(r)))).all()
return self._build_multi_response(events)
except ValueError:
return self.error_response(400, 'Invalid coordinates or radius format')
else:
return self.error_response(400, 'Must include lat, lng, and r in query string')
@transaction.atomic
def post(self, request):
data = json.loads(request.body)
location = data.get('location', {})
coordinates = Point(location.get("lng"), location.get("lat"), srid=4326)
event = Event(
name=data.get('name'),
description=data.get('description'),
url=data.get('url'),
address=data.get('address'),
status=data.get('status'),
price=data.get('price'),
require_rsvp=data.get('require_rsvp'),
start_time=data.get('start_time'),
end_time=data.get('end_time'),
rain_date=data.get('rain_date'),
email=data.get('email'),
phone_number=data.get('phone_number'),
coordinates=coordinates,
)
event.save()
categories = data.get('categories', [])
self._create_event_categories(categories, event)
tag_names = data.get('tags', [])
self._create_event_tags(tag_names, event)
return self._build_response(event)
def _create_event_categories(self, categories, event):
event_categories = []
for category_id in categories:
category = get_object_or_404(Category, pk=category_id)
event_category = EventCategory(
event=event,
category=category
)
event_categories.append(event_category)
EventCategory.objects.bulk_create(event_categories)
def _create_event_tags(self, tag_names, event):
event_tags = []
for tag_name in tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
event_tag = EventTag(
tag=tag,
event=event
)
event_tags.append(event_tag)
EventTag.objects.bulk_create(event_tags)
@transaction.atomic
def put(self, request, event_id):
data = json.loads(request.body)
event = get_object_or_404(Event, pk=event_id)
location = data.get('location', {})
if location:
coordinates = Point(location.get("lng"), location.get("lat"), srid=4326)
event.coordinates = coordinates
event.name = data.get('name', event.name)
event.description = data.get('description', event.description)
event.url = data.get('url', event.url)
event.address = data.get('address', event.address)
event.status = data.get('status', event.status)
event.price = data.get('price', event.price)
event.require_rsvp = data.get('require_rsvp', event.require_rsvp)
event.start_time = data.get('start_time', event.start_time)
event.end_time = data.get('end_time', event.end_time)
event.rain_date = data.get('rain_date', event.rain_date)
event.email = data.get('email', event.email)
event.phone_number = data.get('phone_number', event.phone_number)
categories = data.get('categories', [])
self._update_event_categories(categories, event)
tag_names = data.get('tags', [])
self._update_event_tags(tag_names, event)
event.save()
self._build_response(event)
def _update_event_categories(self, categories, event):
if categories:
current_event_categories = EventCategory.objects.filter(event=event).filter(active=True).all()
current_event_category_map = {ec.category.id: ec for ec in current_event_categories}
for current_event_category in current_event_categories:
if current_event_category.category.id not in categories:
current_event_category.active = False
current_event_category.save()
new_event_categories = []
for category_id in categories:
category = get_object_or_404(Category, pk=category_id)
if category not in current_event_category_map:
event_category = EventCategory(
category=category,
event=event
)
new_event_categories.append(event_category)
if new_event_categories:
EventCategory.objects.bulk_create(new_event_categories)
def _update_event_tags(self, tag_names, event):
if tag_names:
current_event_tags = EventTag.objects.filter(event=event).filter(active=True).all()
current_event_tag_names = [x.name for x in current_event_tags]
for current_event_tag in current_event_tags:
if current_event_tag.tag.name not in tag_names:
current_event_tag.active = False
current_event_tag.save()
new_event_tags = []
for tag_name in tag_names:
if tag_name not in current_event_tag_names:
tag, created = Tag.objects.get_or_create(name=tag_name)
event_tag = EventTag(
tag=tag,
event=event
)
new_event_tags.append(event_tag)
if new_event_tags:
EventTag.objects.bulk_create(new_event_tags)
def delete(self, request, event_id):
event = get_object_or_404(Event, pk=event_id)
event.active = False
event.save()
return self._build_response(event)

42
api/views/tag.py Normal file
View file

@ -0,0 +1,42 @@
import json
from django.shortcuts import get_object_or_404
from api.serializers.tag import TagSerializer
from api.views.base import BaseView
from web.models.tag import Tag
class TagView(BaseView):
SERIALIZER = TagSerializer
def get(self, request, tag_id=None):
if tag_id:
tag = get_object_or_404(Tag, pk=tag_id)
return self._build_response(tag)
else:
tags = Tag.objects.filter(active=True).all()
return self._build_multi_response(tags)
def post(self, request):
data = json.loads(request.body)
tag = Tag(
name=data.get('name'),
)
tag.save()
return self._build_response(tag)
def put(self, request, tag_id):
data = json.loads(request.body)
tag = get_object_or_404(Tag, pk=tag_id)
tag.name = data.get('name', tag.name)
tag.save()
self._build_response(tag)
def delete(self, request, tag_id):
tag = get_object_or_404(Tag, pk=tag_id)
tag.active = False
tag.save()
return self._build_response(tag)

Binary file not shown.

View file

@ -1,16 +1,5 @@
"""
Django settings for localist project.
Generated by 'django-admin startproject' using Django 6.0.6.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/6.0/ref/settings/
"""
from pathlib import Path
from config import DB_NAME, DB_USER, DB_HOST, DB_PASSWORD, DB_PORT, SECRET_KEY
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@ -20,7 +9,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-pek1teheggj8zzvtcntp4-u#s_^yc$&a@3f7wi+u%8)g*2xezw"
SECRET_KEY = SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
@ -31,17 +20,23 @@ ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"corsheaders",
"django.contrib.gis",
"leaflet",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"web",
"api",
]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
@ -76,8 +71,12 @@ WSGI_APPLICATION = "localist.wsgi.application"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
"ENGINE": "django.contrib.gis.db.backends.postgis",
"NAME": DB_NAME,
"USER": DB_USER,
"PASSWORD": DB_PASSWORD,
"HOST": DB_HOST,
"PORT": DB_PORT
}
}
@ -117,3 +116,9 @@ USE_TZ = True
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = "static/"
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
"http://127.0.0.1:5173",
]

View file

@ -1,23 +1,16 @@
"""
URL configuration for localist project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/6.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from api.views.event import EventView
from api.views.category import CategoryView
from api.views.tag import TagView
urlpatterns = [
path("admin/", admin.site.urls),
path("api/event/<int:event_id>", EventView.as_view(), name="event"),
path("api/event", EventView.as_view(), name="event"),
path("api/category/<int:category_id>", CategoryView.as_view(), name="category"),
path("api/category", CategoryView.as_view(), name="category"),
path("api/tag/<int:tag_id>", TagView.as_view(), name="tag"),
path("api/tag", TagView.as_view(), name="tag"),
]

23
manage.py Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "localist.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()

14
requirements.txt Normal file
View file

@ -0,0 +1,14 @@
asgiref==3.11.1
certifi==2026.5.20
charset-normalizer==3.4.7
Django==6.0.6
django-cors-headers==4.0.0
django-leaflet==0.33.0
django-rest-framework==0.1.0
djangorestframework==3.17.1
idna==3.18
psycopg==3.3.4
psycopg-binary==3.3.4
requests==2.34.2
sqlparse==0.5.5
urllib3==2.7.0

View file

@ -0,0 +1,11 @@
import requests
url = "http://127.0.0.1:8000/api/category"
data = {
"name": "Test Category",
"description": "this is a test",
}
resp = requests.post(url, data=data)
print(resp.status_code)

View file

@ -0,0 +1,10 @@
import requests
url = "http://127.0.0.1:8000/api/category/1"
data = {
"name": "diddy cat"
}
resp = requests.put(url, data=data)
print(resp.status_code)

View file

@ -0,0 +1,24 @@
import requests
url = "http://127.0.0.1:8000/api/event"
data = {
"name": "Test Event 3",
"description": "this is a test",
"url": "https://www.domdit.com",
"address": "31 Fleetwood Dr Hazlet Nj",
"status": "scheduled",
"price": "10.00",
"require_rsvp": False,
"start_time": "2026-06-14T01:34:39Z",
"end_time": "2026-08-14T01:34:38Z",
"rain_date": "2026-06-15T01:34:42Z",
"email": "me@domdit.com",
"phone_number": "",
"categories": [1],
"tags": ["tag1", "tag2", "tag4"]
}
resp = requests.post(url, json=data)
print(resp.status_code)

View file

@ -0,0 +1,10 @@
import requests
url = "http://127.0.0.1:8000/api/event/1"
data = {
"name": "diddy freakoff",
}
resp = requests.put(url, data=data)
print(resp.status_code)

View file

0
test_requests/tag_put.py Normal file
View file

Binary file not shown.

View file

@ -5,6 +5,7 @@ from web.models.event_category import EventCategory, EventCategoryAdmin
from web.models.event_tag import EventTag, EventTagAdmin
from web.models.tag import Tag, TagAdmin
admin.site.register(Category, CategoryAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(EventCategory, EventCategoryAdmin)

View file

@ -0,0 +1,23 @@
# Generated by Django 6.0.6 on 2026-06-21 00:12
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("web", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="eventcategory",
name="category",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="category",
to="web.category",
),
),
]

View file

@ -0,0 +1,21 @@
# Generated by Django 6.0.6 on 2026-06-21 17:56
import django.contrib.gis.db.models.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("web", "0002_alter_eventcategory_category"),
]
operations = [
migrations.AddField(
model_name="event",
name="coordinates",
field=django.contrib.gis.db.models.fields.PointField(
blank=True, default=None, null=True, srid=4326
),
),
]

View file

@ -0,0 +1,21 @@
# Generated by Django 6.0.6 on 2026-06-21 19:33
import django.contrib.gis.db.models.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("web", "0003_event_coordinates"),
]
operations = [
migrations.AlterField(
model_name="event",
name="coordinates",
field=django.contrib.gis.db.models.fields.PointField(
blank=True, default=None, geography=True, null=True, srid=4326
),
),
]

View file

@ -1,8 +1,7 @@
from django.db import models
from django.contrib import admin
from leaflet.admin import LeafletGeoAdmin
from django.contrib.gis.db import models
from web.models.base import BaseModel
from enum import StrEnum
class Event(BaseModel):
@ -15,6 +14,7 @@ class Event(BaseModel):
description = models.TextField()
url = models.URLField()
address = models.CharField()
coordinates = models.PointField(blank=True, default=None, null=True, srid=4326, geography=True)
status = models.CharField(max_length=20, choices=Status.choices, default=Status.SCHEDULED)
price = models.DecimalField(max_digits=10, default=None, blank=True, decimal_places=2)
require_rsvp = models.BooleanField()
@ -30,7 +30,7 @@ class Event(BaseModel):
db_table = 'events'
class EventAdmin(admin.ModelAdmin):
class EventAdmin(LeafletGeoAdmin):
search_fields = (
'name',
'description',
@ -39,6 +39,7 @@ class EventAdmin(admin.ModelAdmin):
'rain_date',
'url',
'address',
'coordinates',
'status',
'price',
'require_rsvp',
@ -52,6 +53,7 @@ class EventAdmin(admin.ModelAdmin):
'rain_date',
'url',
'address',
'coordinates',
'status',
'price',
'require_rsvp',

View file

@ -7,7 +7,7 @@ from web.models.category import Category
class EventCategory(BaseModel):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category")
class Meta:
db_table = 'event_categories'

View file

@ -1,3 +0,0 @@
from django.shortcuts import render
# Create your views here.