Search code examples
djangomodelsrelationships

How do I link these two models such that one will update that same instance?


I really want to build this app with Django that lets people register and create User instances that can be edited. Each User instance is already linked to a UserProfile with OneToOne because I didn't want to mess with the original User model. The UserProfile will have a field where he/she can register a game if that person is logged in.

ie. Billy wants to register for Monday Smash Melee. He logs in, clicks an option on a form, the UserProfile linked to User, Billy, will update the registered game choice and user tag to the user profile.

The part with the user profile linking to the user works fine, but I don't know how to update the UserProfile with the new tournament registration form so that it can change the UserProfile fields that's linked to the user that is logged in.

Django Models:

class UserProfile(models.Model):
#User profile for registered users. SEPARATE USERBASE TO PLAYER_RANKING
#To Do: add more customizeability and more access for registered.
#weekly e-mails, ability to register for weeklies...
user = models.OneToOneField(User)
picture = models.ImageField(upload_to='profile_images', blank=True)


MON = 'ME'
TUE = 'S4'
THR = 'PM'
reg_game_choices = (
    (MON, "Melee"),
    (TUE, "Smash 4"),
    (THR, "PM"),
    )
reg_game_choice = models.CharField(max_length=2,
                                   choices=reg_game_choices,
                                   default="")
user_tag = models.CharField(max_length=60, default = "")

def __str__(self):
    return self.user.username

Forms:

class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())

class Meta:
    model = User
    fields = ('username', 'password')

class UserProfileForm(forms.ModelForm):
    class Meta:
        model = UserProfile
        fields = ('picture',)

class TournyRegForm(forms.ModelForm):
    class Meta:
        model = UserProfile
        fields = ('reg_game_choice', 'user_tag',)

View:

@login_required
def tourny_reg(request):
    #Registering for tournaments
    context_dict = {}

    weekday = datetime.datetime.today().weekday()
    day_names = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']
    game_days = ['SMASH MELEE', 'SMASH 4', 'CLOSED', 'PROJECT M &     FIGHTING GAMES',
            'FRIENDLIES', 'CLOSED', 'CLOSED']
    day_title = day_names[weekday]
    game_day = game_days[weekday]


    context_dict['day'] = day_title
    context_dict['game_of_the_day'] = game_day

    if request.method == 'POST':
        tourny_form = TournyRegForm(data=request.POST)
        if tourny_form.is_valid():
            tourny_form.save()
        else:
            print (tourny_form.errors)
    else:
        tourny_form = TournyRegForm()

    context_dict['tourny_form'] = tourny_form



return render(request, 'Kappa/tourny_reg.html', context_dict)

It shows up perfectly fine in html and on the local server, but when I try, it gives me an integrity error.

IntegrityError at /Kappa/tourny_reg/

NOT NULL constraint failed: Kappa_userprofile.user_id Exception Value:

NOT NULL constraint failed: Kappa_userprofile.user_id ▶ Local vars C:\Users\Kyle\Documents\GitHub\Kappa_Ranks\Kappa\views.py in tourny_reg


Solution

  • So basically, you want to know how to save an instance of something which is related to the logged-in user. That's easy.

    To explain why you are getting a NOT NULL error: Your TournyRegForm class has not been told to display an input field for 'user', so it isn't. So when you go to save the form, None is being filled in for the user field. The database is complaining because a 'NOT NULL' field has a NULL value, which is a problem.. so this error is legitimate.

    But it's ok that this field is not on the form.. because you don't want the user telling us who they are via the form, you want to get the information about who they are by the fact that they are logged in. The Django auth module puts this information in the Request object where you can easily get at it. All you need to do is to fill in the correct user before the model is saved, like this:

    if tourny_form.is_valid():
        # commit= False tells the modelform to just create the model instance
        # but don't save it yet.
        user_profile = tourny_form.save(commit=False)
        # associate this user_profile with the logged in user.. it is always
        # present in the request object if you are using django's         auth module.
        user_profile.user = request.user
        # now save it
        user_profile.save()
    

    So that takes care of saving a model that is related to the currently logged in user. But you have other problems. For example, do you want to save a new UserProfile each time? I don't think you do.. So on your GET you need to do something like this:

    user_profile = UserProfile.objects.filter(user=request.user).first()
    tourny_form = TournyRegForm(instance=user_profile)
    

    This will fetch the UserProfile of the currently logged=in user from the database, then initialize the form with that instance, so when the user comes back they will be able to edit their profile.

    Now, if you actually want the user to be able to register for multiple games.. you will need a Game model for storing the game information, with one-to-many relationship with your UserProfile. This works by having a ForeignKey field in the Game model which relates it to UserProfile.. so each user will have only one UserProfile but could have multiple Games.