Skip to content

Commit af6e63c

Browse files
authored
feat: improve development workflow with sample data generation tooling
Add comprehensive sample data generation improvements: - Add development.env for Docker environment configuration - Add django:generate-sample-data task to Taskfile - Update README with sample data generation instructions - Refactor factories to use proper factory-boy DjangoModelFactory classes (SponsorshipLevelFactory, MeetupFactory) with Sequence and LazyFunction - Enhance generate_sample_data management command - Add wagtail-factories dependency and regenerate requirements lockfile This makes it easier for contributors to set up a working development environment with realistic test data. PR #192 Co-authored-by: iShelar <51042360+iShelar@users.noreply.github.com>
1 parent e811ab8 commit af6e63c

File tree

7 files changed

+176
-19
lines changed

7 files changed

+176
-19
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ Website for Python Ireland (python.ie / pycon.ie) community, built with Django 6
3030

3131
4. Generate sample data (creates pages, navigation, meetups):
3232
```bash
33-
docker compose run --rm web python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev
33+
task django:generate-sample-data
34+
# or: docker compose run --rm web python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev
3435
```
3536

3637
5. Create a superuser:
@@ -58,7 +59,7 @@ If you prefer to develop without Docker:
5859
5. Activate the virtualenv: `source pythonie-venv/bin/activate`
5960
6. Install dependencies: `pip install -r requirements.txt` (or `uv pip install -r requirements.txt`)
6061
7. Set up the database: `python pythonie/manage.py migrate --settings=pythonie.settings.dev`
61-
8. Generate sample data: `python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev`
62+
8. Generate sample data: `task django:generate-sample-data` (or `python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev`)
6263
9. Create a superuser: `python pythonie/manage.py createsuperuser --settings=pythonie.settings.dev`
6364
10. Install and run Redis server locally: `redis-server`
6465
11. Set Redis environment variable: `export REDISCLOUD_URL=127.0.0.1:6379`
@@ -95,7 +96,7 @@ task django:make-migrations # Create new migrations
9596
task django:collect-static # Collect static files
9697

9798
# Sample Data (for development)
98-
python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev
99+
task django:generate-sample-data
99100

100101
# Testing
101102
task tests # Run test suite

Taskfile.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,16 @@ tasks:
145145
cmds:
146146
- docker compose run --rm web python pythonie/manage.py collectstatic
147147

148+
django:generate-sample-data:
149+
desc: Generate sample data for development
150+
cmds:
151+
- |
152+
if command -v docker >/dev/null 2>&1 && docker ps >/dev/null 2>&1; then
153+
docker compose run --rm web python pythonie/manage.py generate_sample_data
154+
else
155+
python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev
156+
fi
157+
148158
dependencies:compute:
149159
desc: Compute the dependencies
150160
cmds:

development.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
DJANGO_SETTINGS_MODULE=pythonie.settings.dev
2+
PGDATABASE=pythonie
3+
PGUSER=postgres
4+
PGPASSWORD=pythonie
5+
PGHOST=postgres
6+
REDISCLOUD_URL=redis://redis:6379
7+

pythonie/core/factories.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import factory
22
from django.utils import timezone
33
from factory.django import DjangoModelFactory
4-
from meetups.models import Meetup
5-
from sponsors.models import SponsorshipLevel
4+
import wagtail_factories
65

76
from core.models import HomePage, SimplePage
7+
from meetups.models import Meetup
8+
from sponsors.models import SponsorshipLevel
89

910

1011
class SponsorshipLevelFactory(DjangoModelFactory):
@@ -32,7 +33,7 @@ class Meta:
3233
visibility = "public"
3334

3435

35-
class HomePageFactory(DjangoModelFactory):
36+
class HomePageFactory(wagtail_factories.PageFactory):
3637
class Meta:
3738
model = HomePage
3839

@@ -42,7 +43,7 @@ class Meta:
4243
body = []
4344

4445

45-
class SimplePageFactory(DjangoModelFactory):
46+
class SimplePageFactory(wagtail_factories.PageFactory):
4647
class Meta:
4748
model = SimplePage
4849

pythonie/core/management/commands/generate_sample_data.py

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,47 @@ def _create_home_page(self):
4141
self.stdout.write("Home page already exists")
4242
return home
4343

44-
wagtail_root = Page.objects.get(depth=1)
44+
try:
45+
wagtail_root = Page.objects.get(depth=1)
46+
except Page.DoesNotExist:
47+
from wagtail.models import Locale
48+
49+
locale, _ = Locale.objects.get_or_create(language_code="en")
50+
wagtail_root = Page.add_root(
51+
instance=Page(title="Root", slug="root", locale=locale)
52+
)
53+
self.stdout.write("Created Wagtail root page")
54+
4555
default_home_exists = Page.objects.filter(slug="home", depth=2).exists()
4656
slug = "python-ireland" if default_home_exists else "home"
4757

48-
home = HomePageFactory.build(
58+
home = HomePageFactory(
59+
parent=wagtail_root,
4960
slug=slug,
5061
show_in_menus=True,
5162
body=self._get_home_content(),
5263
)
53-
wagtail_root.add_child(instance=home)
54-
self.stdout.write(self.style.SUCCESS("Created home page"))
55-
56-
site = Site.objects.filter(is_default_site=True).first()
57-
if site:
64+
# Publish the home page
65+
revision = home.save_revision()
66+
revision.publish()
67+
self.stdout.write(self.style.SUCCESS("Created and published home page"))
68+
69+
# Create or update the default site
70+
site, created = Site.objects.get_or_create(
71+
is_default_site=True,
72+
defaults={
73+
"hostname": "localhost",
74+
"port": 8000,
75+
"site_name": "Python Ireland",
76+
"root_page": home,
77+
},
78+
)
79+
if not created:
5880
site.root_page = home
5981
site.save()
6082
self.stdout.write(self.style.SUCCESS("Updated site root page"))
83+
else:
84+
self.stdout.write(self.style.SUCCESS("Created default site"))
6185

6286
return home
6387

@@ -88,10 +112,13 @@ def _create_page(self, parent, title, slug, body=None):
88112
self.stdout.write(f" {title} already exists")
89113
return SimplePage.objects.get(slug=slug)
90114

91-
page = SimplePageFactory.build(
92-
title=title, slug=slug, body=body or [], show_in_menus=True
115+
page = SimplePageFactory(
116+
parent=parent,
117+
title=title,
118+
slug=slug,
119+
body=body or [],
120+
show_in_menus=True,
93121
)
94-
parent.add_child(instance=page)
95122
self.stdout.write(self.style.SUCCESS(f"Created {title}"))
96123
return page
97124

requirements/dev.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
-c main.txt
33
coverage
44
django-debug-toolbar
5-
factory-boy
5+
wagtail-factories
66
fakeredis
77
isort
88
model_mommy

requirements/dev.txt

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
# This file was autogenerated by uv via the following command:
22
# uv pip compile --output-file requirements/dev.txt requirements/dev.in
3+
anyascii==0.3.3
4+
# via
5+
# -c requirements/main.txt
6+
# wagtail
37
asgiref==3.11.0
48
# via
59
# -c requirements/main.txt
610
# django
11+
beautifulsoup4==4.14.3
12+
# via
13+
# -c requirements/main.txt
14+
# wagtail
715
boolean-py==5.0
816
# via license-expression
917
cachecontrol==0.14.4
@@ -24,27 +32,88 @@ defusedxml==0.7.1
2432
# via
2533
# -c requirements/main.txt
2634
# py-serializable
35+
# willow
2736
django==6.0.1
2837
# via
2938
# -c requirements/main.txt
3039
# django-debug-toolbar
40+
# django-filter
41+
# django-modelcluster
42+
# django-permissionedforms
43+
# django-stubs-ext
44+
# django-taggit
45+
# django-tasks
46+
# django-treebeard
47+
# djangorestframework
48+
# laces
3149
# model-mommy
50+
# modelsearch
51+
# wagtail
3252
django-debug-toolbar==6.1.0
3353
# via -r requirements/dev.in
54+
django-filter==25.2
55+
# via
56+
# -c requirements/main.txt
57+
# wagtail
58+
django-modelcluster==6.4.1
59+
# via
60+
# -c requirements/main.txt
61+
# wagtail
62+
django-permissionedforms==0.1
63+
# via
64+
# -c requirements/main.txt
65+
# wagtail
66+
django-stubs-ext==5.2.8
67+
# via
68+
# -c requirements/main.txt
69+
# django-tasks
70+
django-taggit==6.1.0
71+
# via
72+
# -c requirements/main.txt
73+
# wagtail
74+
django-tasks==0.9.0
75+
# via
76+
# -c requirements/main.txt
77+
# modelsearch
78+
# wagtail
79+
django-treebeard==4.8.0
80+
# via
81+
# -c requirements/main.txt
82+
# wagtail
83+
djangorestframework==3.16.1
84+
# via
85+
# -c requirements/main.txt
86+
# wagtail
87+
draftjs-exporter==5.2.0
88+
# via
89+
# -c requirements/main.txt
90+
# wagtail
91+
et-xmlfile==2.0.0
92+
# via
93+
# -c requirements/main.txt
94+
# openpyxl
3495
factory-boy==3.3.3
35-
# via -r requirements/dev.in
96+
# via wagtail-factories
3697
faker==40.1.0
3798
# via factory-boy
3899
fakeredis==2.33.0
39100
# via -r requirements/dev.in
40101
filelock==3.20.3
41102
# via cachecontrol
103+
filetype==1.2.0
104+
# via
105+
# -c requirements/main.txt
106+
# willow
42107
idna==3.11
43108
# via
44109
# -c requirements/main.txt
45110
# requests
46111
isort==7.0.0
47112
# via -r requirements/dev.in
113+
laces==0.1.2
114+
# via
115+
# -c requirements/main.txt
116+
# wagtail
48117
license-expression==30.4.4
49118
# via cyclonedx-python-lib
50119
markdown-it-py==4.0.0
@@ -53,8 +122,16 @@ mdurl==0.1.2
53122
# via markdown-it-py
54123
model-mommy==2.0.0
55124
# via -r requirements/dev.in
125+
modelsearch==1.1.1
126+
# via
127+
# -c requirements/main.txt
128+
# wagtail
56129
msgpack==1.1.2
57130
# via cachecontrol
131+
openpyxl==3.1.5
132+
# via
133+
# -c requirements/main.txt
134+
# wagtail
58135
packageurl-python==0.17.6
59136
# via cyclonedx-python-lib
60137
packaging==25.0
@@ -63,6 +140,15 @@ packaging==25.0
63140
# pip-audit
64141
# pip-requirements-parser
65142
# pipdeptree
143+
pillow==12.1.0
144+
# via
145+
# -c requirements/main.txt
146+
# pillow-heif
147+
# wagtail
148+
pillow-heif==1.1.1
149+
# via
150+
# -c requirements/main.txt
151+
# willow
66152
pip==25.3
67153
# via
68154
# pip-api
@@ -92,6 +178,7 @@ requests==2.32.5
92178
# -c requirements/main.txt
93179
# cachecontrol
94180
# pip-audit
181+
# wagtail
95182
rich==14.2.0
96183
# via pip-audit
97184
ruff==0.14.11
@@ -100,15 +187,29 @@ sortedcontainers==2.4.0
100187
# via
101188
# cyclonedx-python-lib
102189
# fakeredis
190+
soupsieve==2.8.1
191+
# via
192+
# -c requirements/main.txt
193+
# beautifulsoup4
103194
sqlparse==0.5.5
104195
# via
105196
# -c requirements/main.txt
106197
# django
107198
# django-debug-toolbar
199+
telepath==0.3.1
200+
# via
201+
# -c requirements/main.txt
202+
# wagtail
108203
tomli==2.3.0
109204
# via pip-audit
110205
tomli-w==1.2.0
111206
# via pip-audit
207+
typing-extensions==4.15.0
208+
# via
209+
# -c requirements/main.txt
210+
# beautifulsoup4
211+
# django-stubs-ext
212+
# django-tasks
112213
tzdata==2025.3
113214
# via
114215
# -c requirements/main.txt
@@ -119,3 +220,13 @@ urllib3==2.6.3
119220
# requests
120221
uv==0.9.24
121222
# via -r requirements/dev.in
223+
wagtail==7.2.1
224+
# via
225+
# -c requirements/main.txt
226+
# wagtail-factories
227+
wagtail-factories==4.3.0
228+
# via -r requirements/dev.in
229+
willow==1.12.0
230+
# via
231+
# -c requirements/main.txt
232+
# wagtail

0 commit comments

Comments
 (0)