Skip to content

Commit 759a5ab

Browse files
authored
Merge pull request #155 from pythonkr/feature/sponsor
후원사 구좌 및 혜택 관리 API 추가
2 parents f0ea6f5 + 45347c8 commit 759a5ab

10 files changed

+395
-27
lines changed

pyconkr/settings-localtest.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22

3-
from pyconkr.settings import *
3+
from pyconkr.settings import * # noqa
44

55
DEBUG = True
66

@@ -10,10 +10,15 @@
1010

1111

1212
# RDS
13-
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}
13+
DATABASES = {
14+
"default": {
15+
"ENGINE": "django.db.backends.sqlite3",
16+
"NAME": BASE_DIR / "local.sqlite3",
17+
}
18+
}
1419

1520
# django-storages: TODO fix to in memory?
16-
del MEDIA_ROOT
21+
del MEDIA_ROOT # noqa
1722
DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
1823
STATICFILES_STORAGE = "storages.backends.s3boto3.S3StaticStorage"
1924
AWS_S3_ACCESS_KEY_ID = os.getenv("AWS_S3_ACCESS_KEY_ID")

pyconkr/settings.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
For the full list of settings and their values, see
1010
https://docs.djangoproject.com/en/4.1/ref/settings/
1111
"""
12+
1213
import os
1314
import pathlib
1415

@@ -81,9 +82,10 @@
8182
TEMPLATES = [
8283
{
8384
"BACKEND": "django.template.backends.django.DjangoTemplates",
84-
"DIRS": [BASE_DIR / "templates" ,
85-
BASE_DIR / "accounts/templates",
86-
],
85+
"DIRS": [
86+
BASE_DIR / "templates",
87+
BASE_DIR / "accounts/templates",
88+
],
8789
"APP_DIRS": True,
8890
"OPTIONS": {
8991
"context_processors": [
@@ -139,7 +141,9 @@
139141
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators
140142

141143
AUTH_PASSWORD_VALIDATORS = [
142-
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
144+
{
145+
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
146+
},
143147
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
144148
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
145149
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
@@ -219,7 +223,7 @@
219223
"https://2023.pycon.kr",
220224
"https://pycon-dev2023.pycon.kr",
221225
"https://pycon-prod2023.pycon.kr",
222-
"https://ticket-2023.pycon.kr", # PG 심사 대비 임시 도메인
226+
"https://ticket-2023.pycon.kr", # PG 심사 대비 임시 도메인
223227
"https://127.0.0.1:3000",
224228
"https://localhost:3000",
225229
"http://2023.pycon.kr",
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Generated by Django 4.1.5 on 2024-07-28 12:17
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("sponsor", "0005_patron"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="SponsorBenefit",
16+
fields=[
17+
(
18+
"id",
19+
models.BigAutoField(
20+
auto_created=True,
21+
primary_key=True,
22+
serialize=False,
23+
verbose_name="ID",
24+
),
25+
),
26+
("name", models.CharField(help_text="혜택 이름", max_length=255)),
27+
("desc", models.TextField(blank=True, help_text="기타", null=True)),
28+
("offer", models.PositiveIntegerField(help_text="제공 하는 혜택 개수")),
29+
("unit", models.CharField(help_text="혜택 단위", max_length=10)),
30+
(
31+
"is_countable",
32+
models.BooleanField(
33+
default=True, help_text="제공 하는 혜택이 셀 수 있는지 여부"
34+
),
35+
),
36+
(
37+
"sponsor_level",
38+
models.ForeignKey(
39+
on_delete=django.db.models.deletion.CASCADE,
40+
related_name="benefits",
41+
to="sponsor.sponsorlevel",
42+
),
43+
),
44+
],
45+
options={
46+
"verbose_name": "후원사 등급 별 혜택",
47+
"verbose_name_plural": "후원사 등급 별 혜택 목록",
48+
},
49+
),
50+
]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Generated by Django 4.1.5 on 2024-08-06 12:41
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("sponsor", "0006_sponsorbenefit"),
11+
]
12+
13+
operations = [
14+
migrations.RemoveField(
15+
model_name="sponsorbenefit",
16+
name="offer",
17+
),
18+
migrations.RemoveField(
19+
model_name="sponsorbenefit",
20+
name="sponsor_level",
21+
),
22+
migrations.CreateModel(
23+
name="BenefitByLevel",
24+
fields=[
25+
(
26+
"id",
27+
models.BigAutoField(
28+
auto_created=True,
29+
primary_key=True,
30+
serialize=False,
31+
verbose_name="ID",
32+
),
33+
),
34+
("offer", models.PositiveIntegerField(help_text="제공 하는 혜택 개수")),
35+
(
36+
"benefit",
37+
models.ForeignKey(
38+
on_delete=django.db.models.deletion.CASCADE,
39+
related_name="benefit_by_level",
40+
to="sponsor.sponsorbenefit",
41+
),
42+
),
43+
(
44+
"level",
45+
models.ForeignKey(
46+
on_delete=django.db.models.deletion.CASCADE,
47+
related_name="benefit_by_level",
48+
to="sponsor.sponsorlevel",
49+
),
50+
),
51+
],
52+
),
53+
migrations.AddConstraint(
54+
model_name="benefitbylevel",
55+
constraint=models.UniqueConstraint(
56+
fields=("benefit_id", "level_id"), name="IX_BENEFIT_BY_LEVEL_1"
57+
),
58+
),
59+
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Generated by Django 4.1.5 on 2024-08-06 13:06
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("sponsor", "0006_sponsorlevel_year"),
10+
("sponsor", "0007_remove_sponsorbenefit_offer_and_more"),
11+
]
12+
13+
operations = []
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Generated by Django 4.1.5 on 2024-08-06 14:27
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("sponsor", "0008_merge_20240806_2206"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="sponsorbenefit",
15+
name="year",
16+
field=models.IntegerField(default=2023),
17+
),
18+
migrations.AddField(
19+
model_name="sponsorlevel",
20+
name="benefits",
21+
field=models.ManyToManyField(
22+
related_name="level",
23+
through="sponsor.BenefitByLevel",
24+
to="sponsor.sponsorbenefit",
25+
),
26+
),
27+
]

sponsor/models.py

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,32 @@ def get_queryset(self):
1010
return super(SponsorLevelManager, self).get_queryset().all().order_by("order")
1111

1212

13+
class SponsorBenefit(models.Model):
14+
class Meta:
15+
verbose_name = "후원사 등급 별 혜택"
16+
verbose_name_plural = "후원사 등급 별 혜택 목록"
17+
18+
name = models.CharField(max_length=255, help_text="혜택 이름")
19+
desc = models.TextField(null=True, blank=True, help_text="기타")
20+
unit = models.CharField(max_length=10, help_text="혜택 단위")
21+
year = models.IntegerField(default=2023)
22+
is_countable = models.BooleanField(
23+
default=True, help_text="제공 하는 혜택이 셀 수 있는지 여부"
24+
)
25+
26+
1327
class SponsorLevel(models.Model):
1428
class Meta:
1529
verbose_name = "후원사 등급"
1630
verbose_name_plural = "후원사 등급"
1731

18-
name = models.CharField(max_length=255, blank=True, default="", help_text="후원 등급명")
32+
name = models.CharField(
33+
max_length=255, blank=True, default="", help_text="후원 등급명"
34+
)
1935
desc = models.TextField(
20-
null=True, blank=True, help_text="후원 혜택을 입력하면 될 거 같아요 :) 후원사가 등급을 정할 때 볼 문구입니다."
36+
null=True,
37+
blank=True,
38+
help_text="후원 혜택을 입력하면 될 거 같아요 :) 후원사가 등급을 정할 때 볼 문구입니다.",
2139
)
2240
visible = models.BooleanField(default=True)
2341
price = models.IntegerField(default=0)
@@ -27,6 +45,10 @@ class Meta:
2745
updated_at = models.DateTimeField(auto_now=True)
2846
year = models.IntegerField(default=2023)
2947

48+
benefits = models.ManyToManyField(
49+
SponsorBenefit, through="BenefitByLevel", related_name="level"
50+
)
51+
3052
objects = SponsorLevelManager()
3153

3254
@property
@@ -51,6 +73,23 @@ def __str__(self):
5173
return self.name
5274

5375

76+
class BenefitByLevel(models.Model):
77+
class Meta:
78+
constraints = [
79+
models.UniqueConstraint(
80+
fields=["benefit_id", "level_id"], name="IX_BENEFIT_BY_LEVEL_1"
81+
)
82+
]
83+
84+
benefit = models.ForeignKey(
85+
SponsorBenefit, on_delete=models.CASCADE, related_name="benefit_by_level"
86+
)
87+
level = models.ForeignKey(
88+
SponsorLevel, on_delete=models.CASCADE, related_name="benefit_by_level"
89+
)
90+
offer = models.PositiveIntegerField(help_text="제공 하는 혜택 개수")
91+
92+
5493
def registration_file_upload_to(instance, filename):
5594
return f"sponsor/business_registration/{instance.id}/{filename}"
5695

@@ -78,7 +117,8 @@ class Meta:
78117
related_name="sponsor_creator",
79118
)
80119
name = models.CharField(
81-
max_length=255, help_text="후원사의 이름입니다. 서비스나 회사 이름이 될 수 있습니다."
120+
max_length=255,
121+
help_text="후원사의 이름입니다. 서비스나 회사 이름이 될 수 있습니다.",
82122
)
83123
level = models.ForeignKey(
84124
SponsorLevel,
@@ -88,13 +128,18 @@ class Meta:
88128
help_text="후원을 원하시는 등급을 선택해주십시오. 모두 판매된 등급은 선택할 수 없습니다.",
89129
)
90130
desc = models.TextField(
91-
null=True, blank=True, help_text="후원사 설명입니다. 이 설명은 국문 홈페이지에 게시됩니다."
131+
null=True,
132+
blank=True,
133+
help_text="후원사 설명입니다. 이 설명은 국문 홈페이지에 게시됩니다.",
92134
)
93135
eng_desc = models.TextField(
94-
null=True, blank=True, help_text="후원사 영문 설명입니다. 이 설명은 영문 홈페이지에 게시됩니다."
136+
null=True,
137+
blank=True,
138+
help_text="후원사 영문 설명입니다. 이 설명은 영문 홈페이지에 게시됩니다.",
95139
)
96140
manager_name = models.CharField(
97-
max_length=100, help_text="준비위원회와 후원과 관련된 논의를 진행할 담당자의 이름을 입력해주십시오."
141+
max_length=100,
142+
help_text="준비위원회와 후원과 관련된 논의를 진행할 담당자의 이름을 입력해주십시오.",
98143
)
99144
manager_email = models.CharField(
100145
max_length=100,
@@ -149,10 +194,13 @@ class Meta:
149194
help_text="사용자가 제출했는지 여부를 저장합니다. 요청이 제출되면 준비위원회에서 검토하고 받아들일지를 결정합니다.",
150195
)
151196
accepted = models.BooleanField(
152-
default=False, help_text="후원사 신청이 접수되었고, 입금 대기 상태인 경우 True로 설정됩니다."
197+
default=False,
198+
help_text="후원사 신청이 접수되었고, 입금 대기 상태인 경우 True로 설정됩니다.",
153199
)
154200
paid_at = models.DateTimeField(
155-
null=True, blank=True, help_text="후원금이 입금된 일시입니다. 아직 입금되지 않았을 경우 None이 들어갑니다."
201+
null=True,
202+
blank=True,
203+
help_text="후원금이 입금된 일시입니다. 아직 입금되지 않았을 경우 None이 들어갑니다.",
156204
)
157205
created_at = models.DateTimeField(auto_now_add=True)
158206
updated_at = models.DateTimeField(auto_now=True)
@@ -161,7 +209,6 @@ def __str__(self):
161209
return f"{self.name}/{self.level}"
162210

163211

164-
165212
class Patron(models.Model):
166213
class Meta:
167214
ordering = ["-total_contribution", "contribution_datetime"]
@@ -177,7 +224,9 @@ class Meta:
177224
help_text="개인후원을 등록한 유저",
178225
related_name="patron_user",
179226
)
180-
total_contribution = models.IntegerField(default=0, help_text="개인후원한 금액입니다.")
227+
total_contribution = models.IntegerField(
228+
default=0, help_text="개인후원한 금액입니다."
229+
)
181230
contribution_datetime = models.DateTimeField(
182231
help_text="개인후원 결제한 일시입니다."
183232
)

0 commit comments

Comments
 (0)