Reputation: 137
I'm creating a WebApp with Django as a personal project. This app tracks hiking challenges and gets the weather from a weather database with an API. My problem is currently with creating a database structure that will be a good design and expandable in the future.
Currently, I have two Tables. (Django Models)
class Mountain(models.Model):
mnt_name = models.CharField(max_length=100)
latitude = models.FloatField()
longitude = models.FloatField()
elevation = models.IntegerField(default=0)
distance = models.IntegerField(default=0)
users_completed = models.ManyToManyField(User)
# Commented from code to prevent issues
# date_done = models.DateTimeField(blank=True, null=True, default=None)
def __str__(self):
return self.mnt_name
class Challenge(models.Model):
challenge_name = models.CharField(max_length=101)
mountains = models.ManyToManyField(Mountain)
def __str__(self):
return self.challenge_name
I have some of the functionality I was looking for. I can loop through the list of users_completed
and compare them against the currently logged in user and if the user is in the list update the completed status. But That isn't a good solution because I cant have user-specific dates completed among other reasons.
Intuitively I thought the best way to go about fixing this issue is to add a copy for each challenge to each user profile so the user can have user-specific data such as the completion status and the date completed. But I'm not sure how I would go about doing that or If it's even possible.
Any suggestions on how I should rearrange my models so that I can have user-specific data such as if the mountain has been completed, and date of completion, etc.
All help is greatly appreciated. Thank you.
Upvotes: 0
Views: 407
Reputation: 431
I would probably use a double through
implementation.
from django.db import models
from django.contrib.auth.models import User
class Mountain(models.Model):
mnt_name = models.CharField(max_length=100)
latitude = models.FloatField()
longitude = models.FloatField()
elevation = models.IntegerField(default=0)
distance = models.IntegerField(default=0)
class MountainChallenge(models.Model):
"""This allows you to use the same mountain in multiple challenges."""
mountain = models.ForeignKey(Mountain, on_delete=models.CASCADE)
challenge = models.ForeignKey("Challenge", on_delete=models.CASCADE)
users_completed = models.ManyToManyField(User, through="UserMountain")
class UserMountain(models.Model):
"""This associates a user to a MoutainChallenge while storing other data."""
user = models.ForeignKey(User, on_delete=models.CASCADE)
mountain_challenge = models.ForeignKey(MountainChallenge, on_delete=models.CASCADE)
date = models.DateTimeField()
class Challenge(models.Model):
challenge_name = models.CharField(max_length=101)
mountains = models.ManyToManyField(Mountain, through="MountainChallenge")
Note I haven't tested this code.
Moutain
objects independently from challenges.MoutainChallenge
which represents a Mountain
as part of a Challenge
ManyToMany
because you want to associate a list of users who have completed the moutainUserMountain
which can hold a date or other things related to the achievementUserMountain
is achieved you should check if the current user has achieved all the MoutainChallenge
s of the current Challenge
Hopefully this makes sense without over-engineering.
I think it leaves room for expansion in the future.
Upvotes: 1
Reputation: 11342
For this, you can use a third table to map the user to the mountain\challenge.
Achievement:
AchievementID
UserID
MountainID -- set when mountain completed
ChallengeID -- set when full challenge completed
AchievementDate
An entry is created when a user completes a mountain or a full challenge.
Upvotes: 1