Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/epistolary event model updates #27

Merged
merged 10 commits into from
Feb 21, 2024

Conversation

XanderVertegaal
Copy link
Contributor

@XanderVertegaal XanderVertegaal commented Feb 12, 2024

Resolves #9, #23.

This PR

  • adds a Gift model, with foreign keys to an associated 'giver' and a letter action;
  • adds a WorldEvent model, with a foreign key to an epistolary event. In the future, the team may decide to attach world events to letter actions, but for now, epistolary events feel like a good place to start.
  • adds a computed property display_date on the abstract LetterCraftDate model;
  • adds instigator to the list of possible Roles.

@XanderVertegaal XanderVertegaal marked this pull request as ready for review February 12, 2024 07:57
@@ -4,4 +4,4 @@
class EventConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'event'
verbose_name = 'epistolary episodes and actions'
verbose_name = 'events and actions'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change is just for consistency, but in general, I think "episode/action" is clearer terminology than "event/action".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My reasoning was that the term 'episodes' is otherwise not (yet) used, so it didn't make much sense to include it here. I agree that 'events' is a bit vague, so if you have any suggestions for terms that encapsulate both world and epistolary events, let me know!

Comment on lines 200 to 218
class EpistolaryEventTrigger(models.Model):
"""
A relationship between an epistolary event and a world event that triggered it.
"""

epistolary_event = models.ForeignKey(
to=EpistolaryEvent,
on_delete=models.CASCADE,
help_text="The epistolary event that was triggered by a world event",
)

world_event = models.ForeignKey(
to=WorldEvent,
on_delete=models.CASCADE,
help_text="The world event that triggered an epistolary event",
)

def __str__(self):
return f"{self.world_event} triggered {self.epistolary_event}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this model doesn't contain extra metadata about the link, it looks like you don't need it; we can just use ManyToManyField without a through model.

That said, I think this should actually be an subclass of Field right? Then we would have extra properties.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

def __str__(self):
categories = self.categories.all()
category_names = [category.get_value_display()
for category in categories]
category_desc = ", ".join(category_names)
letters = ", ".join(letter.__str__() for letter in self.letters.all())
return f"{category_desc} of {letters}"
return f"{category_desc} of {letters} ({self.date.display_date})"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's useful for the admin overview, we could also add the date to the admin list display.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm interested, but I'm not sure what you mean. Would this be a dynamic label like the date ranges you have added to the Letter admin?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I meant that you could configure the admin class of this model with something like list_display = ['description', 'display_date'] (if description and display_date are database fields or properties with @admin.display() decorators). Then the admin would show the date as a second column in the overview.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, very useful! Good idea.

Comment on lines 190 to 194
epistolary_events = models.ManyToManyField(
to=EpistolaryEvent,
through="EpistolaryEventTrigger",
related_name="triggers",
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logically, epistolary events can also trigger world events, or epistolary events can trigger other epistolary events. Perhaps this requires a more complex relationship?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good point. I'll make the relationship bidirectional.

@XanderVertegaal
Copy link
Contributor Author

Ah, good point. I'll make the relationship bidirectional.

This turned out to be a bit more complicated than I thought (but good practice with working with recursive ManyToManyFields). The new structure can be summarised as follows:

  • Epistolary Events can trigger World Events
  • World Events can trigger Epistolary Events
  • Epistolary Events can trigger other Epistolary Events
  • World events can trigger other World Events

Let me know what you think!


class WorldEventsTriggeredWorldEventsInline(admin.StackedInline):
model = models.WorldEvent.triggered_world_events.through
fk_name = "triggering_world_event"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fk_name is required if there are two references to the same model on the inline model.

triggered_epistolary_events = models.ManyToManyField(
to="self",
through="EpistolaryEventSelfTrigger",
through_fields=("triggering_epistolary_event", "triggered_epistolary_event"),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know (in case you didn't know this -- I didn't!):

  • Without symmetrical=False, Django will assume that any model's ManyToMany relationship with itself is symmetrical and will add two records in the database.
  • through_fields makes sure that when you call world_event.triggered_world_events.add(...<another-world-event>...), this another-world-event is mapped to the correct field in the through model. In other words, it marks which of the two foreign fields in the through model is the source and which is the target.

@XanderVertegaal XanderVertegaal merged commit 9fc9541 into develop Feb 21, 2024
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Model changes for EpistolaryEvent
2 participants