Cryoexn
Cryoexn

Reputation: 137

How do you get unique user data for each user in a Django Web App

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

Answers (2)

romaingz
romaingz

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.

  • You store Moutain objects independently from challenges.
  • You have a MoutainChallenge which represents a Mountain as part of a Challenge
  • It can't be a simple ManyToMany because you want to associate a list of users who have completed the moutain
  • This is done with UserMountain which can hold a date or other things related to the achievement
  • On the business side of your app, when a UserMountain is achieved you should check if the current user has achieved all the MoutainChallenges of the current Challenge

Hopefully this makes sense without over-engineering.

I think it leaves room for expansion in the future.

Upvotes: 1

Mike67
Mike67

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

Related Questions