pkmntrade.club/src/pkmntrade_club/cards/migrations/0001_initial.py
badbl0cks ecb060af6d
feat(cards): Refactor card styling and implement JS multiselect
Refactors card styling by moving color data to the `CardSet` model, removing the `Card.style` and `Pack.hex_color` fields.

- Adds `hex_color` to `CardSet` and a `CardSetColorMapping` model to populate it during import.
- Card importer now uses correct filename regexes to identify sets and apply color mappings.
- Card badge styling is now derived from the `CardSet`'s color, simplifying the data model.

Replaces the old clunky card multiselect with a dynamic Alpine.js component for an improved user experience.

- Introduces an API endpoint (`cards/api/search/`) for asynchronous searching.
- Provides a modern search-as-you-type interface with a `<noscript>` fallback.
2025-06-20 00:30:03 -07:00

771 lines
28 KiB
Python

# Generated by Django 5.1 on 2025-06-20 07:14
import django.db.models.deletion
import parler.fields
import parler.models
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Ability",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Ability",
"verbose_name_plural": "Abilities",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="Attack",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
(
"damage",
models.CharField(
blank=True,
help_text="Damage string, e.g., '40', '20x', '80+'.",
max_length=10,
null=True,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Attack",
"verbose_name_plural": "Attacks",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="CardSet",
fields=[
(
"id",
models.CharField(
help_text="The ID for the set, e.g., 'A1', 'A1a'.",
max_length=3,
primary_key=True,
serialize=False,
),
),
(
"file_name",
models.CharField(
help_text="Original name of the JSON file, e.g., 'a1-genetic-apex.json'.",
max_length=32,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Card Set",
"verbose_name_plural": "Card Sets",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="CardSetColorMapping",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
(
"cardset_id",
models.CharField(
help_text="The cardset ID to match (e.g., 'A1').",
max_length=10,
unique=True,
),
),
(
"hex_color",
models.CharField(
help_text="The hex color code to use for this cardset.",
max_length=9,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Cardset Color Mapping",
"verbose_name_plural": "Cardset Color Mappings",
"ordering": ["cardset_id"],
},
),
migrations.CreateModel(
name="CardType",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Card Type",
"verbose_name_plural": "Card Types",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="Energy",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Energy",
"verbose_name_plural": "Energies",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="Rarity",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("icon", models.CharField(max_length=12)),
("level", models.PositiveIntegerField()),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Rarity",
"verbose_name_plural": "Rarities",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="RarityMapping",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
(
"original_name",
models.CharField(
help_text="The rarity name as it appears in the import source (e.g., JSON file).",
max_length=255,
unique=True,
),
),
(
"mapped_name",
models.CharField(
help_text="The standardized rarity name to use in the system.",
max_length=32,
),
),
(
"icon",
models.CharField(
help_text="The icon associated with this rarity.", max_length=12
),
),
(
"level",
models.PositiveIntegerField(
help_text="The level or order of this rarity."
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
],
options={
"verbose_name": "Rarity Mapping",
"verbose_name_plural": "Rarity Mappings",
"ordering": ["original_name"],
},
),
migrations.CreateModel(
name="AttackCost",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"quantity",
models.PositiveIntegerField(
default=1,
help_text="Quantity of this energy type required for the attack.",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
(
"attack",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="cards.attack"
),
),
(
"energy",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="cards.energy"
),
),
],
options={
"verbose_name": "Attack Cost",
"verbose_name_plural": "Attack Costs",
"unique_together": {("attack", "energy")},
},
),
migrations.AddField(
model_name="attack",
name="energy_cost",
field=models.ManyToManyField(
related_name="attacks", through="cards.AttackCost", to="cards.energy"
),
),
migrations.CreateModel(
name="Pack",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
(
"cardset",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="packs",
to="cards.cardset",
),
),
],
options={
"verbose_name": "Pack",
"verbose_name_plural": "Packs",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="Card",
fields=[
("cardnum", models.AutoField(primary_key=True, serialize=False)),
(
"id",
models.CharField(
db_index=True,
help_text="The unique ID from the JSON source, cardset-cardnum (e.g., 'a1-001').",
max_length=10,
),
),
(
"checksum",
models.CharField(
blank=True,
db_index=True,
help_text="SHA256 checksum of the card data.",
max_length=64,
null=True,
),
),
(
"health",
models.PositiveIntegerField(
blank=True, help_text="HP of the Pokémon.", null=True
),
),
(
"retreat_cost",
models.PositiveIntegerField(
blank=True,
help_text="The number of retreat cost for the card.",
null=True,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
(
"abilities",
models.ManyToManyField(
blank=True, related_name="cards", to="cards.ability"
),
),
(
"attacks",
models.ManyToManyField(related_name="cards", to="cards.attack"),
),
(
"cardset",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="cards",
to="cards.cardset",
),
),
(
"card_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="cards",
to="cards.cardtype",
),
),
(
"pkmn_type",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="cards_pkmn_type",
to="cards.energy",
),
),
(
"weakness_type",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="cards_weakness_type",
to="cards.energy",
),
),
(
"packs",
models.ManyToManyField(related_name="cards", to="cards.pack"),
),
(
"rarity",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="cards",
to="cards.rarity",
),
),
],
options={
"verbose_name": "Card",
"verbose_name_plural": "Cards",
},
bases=(parler.models.TranslatableModelMixin, models.Model),
),
migrations.CreateModel(
name="AbilityTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
help_text="The name of the ability.", max_length=32
),
),
(
"effect",
models.TextField(help_text="Description of the ability's effect."),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.ability",
),
),
],
options={
"verbose_name": "Ability Translation",
"db_table": "cards_ability_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="AttackTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
help_text="The name of the attack.", max_length=32
),
),
(
"effect",
models.TextField(help_text="Description of the attack's effect."),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.attack",
),
),
],
options={
"verbose_name": "Attack Translation",
"db_table": "cards_attack_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="CardSetTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
help_text="The full name of the set, e.g., 'Genetic Apex'.",
max_length=32,
),
),
(
"hex_color",
models.CharField(
blank=True,
help_text="The hex color code associated with this card set.",
max_length=9,
null=True,
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.cardset",
),
),
],
options={
"verbose_name": "Card Set Translation",
"db_table": "cards_cardset_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="CardTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(help_text="The name of the card.", max_length=32),
),
(
"evolves_from_name",
models.CharField(
blank=True,
help_text="Name of the Pokémon this card evolves from.",
max_length=32,
null=True,
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.card",
),
),
],
options={
"verbose_name": "Card Translation",
"db_table": "cards_card_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="CardTypeTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
help_text="The name of the card type.", max_length=32
),
),
(
"subtype",
models.CharField(
blank=True,
help_text="The subtype of the card type.",
max_length=32,
null=True,
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.cardtype",
),
),
],
options={
"verbose_name": "Card Type Translation",
"db_table": "cards_cardtype_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="EnergyTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
help_text="The name of the energy.", max_length=32
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.energy",
),
),
],
options={
"verbose_name": "Energy Translation",
"db_table": "cards_energy_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="PackTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"full_name",
models.CharField(
help_text="The full name of the pack, e.g., 'Genetic Apex: Mewtwo'.",
max_length=32,
),
),
(
"name",
models.CharField(
help_text="The pack name itself, e.g., 'Mewtwo'.", max_length=32
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.pack",
),
),
],
options={
"verbose_name": "Pack Translation",
"db_table": "cards_pack_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
migrations.CreateModel(
name="RarityTranslation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"language_code",
models.CharField(
db_index=True, max_length=15, verbose_name="Language"
),
),
(
"name",
models.CharField(
help_text="The name of the rarity.", max_length=32
),
),
(
"master",
parler.fields.TranslationsForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="translations",
to="cards.rarity",
),
),
],
options={
"verbose_name": "Rarity Translation",
"db_table": "cards_rarity_translation",
"db_tablespace": "",
"managed": True,
"default_permissions": (),
"unique_together": {("language_code", "master")},
},
bases=(parler.models.TranslatedFieldsModelMixin, models.Model),
),
]