简体   繁体   中英

How to save a Model Form with a foreign key of User to django models

I am trying to save user API keys to a Django Model with a foreign key of User such that each set of unique API keys has a unique User associated with them in the database. I want a api keys input form to appear when a user who clicks "account" if the user has no associated API keys and a message saying " Your keys have already been entered" if the user does have associated API keys.

I am using the built in Django User Model and I have created my own Model Form in forms.py.

I have tried including user in the Model form and Meta class to no avail. I have tried passing the keys and password as parameters to the model and form before saving. I have tried using instances of the form with commit set to False before adding the User to form.user. I've tried looking up the latest Django documentation but its fragmented and hard to follow.

I'm purposefully leaving out imports unnecessary to this problem.

This is my Models.py :

from django.db import models
from django.contrib.auth.models import User
from django.conf import settings

# Create your models here.

class UserApiDetails(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=None)
    key = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

    def __str__(self):
        out = "key: " + str(self.key) + '\n' + "Password: " + str(self.password)
        return out

     # this is the method that checks if the user has existing API keys
    def keys_exist(self): 
        if UserApiDetails.objects.filter(pk=User).exists():
            return True
        else:
            return False

This is my forms.py :

from django import forms
from .models import UserApiDetails

class ApiKeysForm(forms.ModelForm):
    key = forms.CharField(max_length=200, label="Api Key")
    password = forms.CharField(max_length=200, widget=forms.PasswordInput, label="Api password")

    class Meta:
        model = UserApiDetails
        fields = {'password', 'key', }

This is my views.py :

from django.shortcuts import render, redirect
from .models import UserApiDetails  # tables in database from models.py
from django.http import JsonResponse, HttpResponse
from django.contrib.auth.models import User
from . import forms
from .forms import ApiKeysForm  # the forms that will get data & pass to views
from django.template.loader import render_to_string

import sys

sys.path.insert(1, '/Development applications/python dev/TradingBot')
# backend imports
from Main import update, trade # methods in bot
from API_comm import ConnAPI  # conn API class used to set keys & session AUTH


# Create your views here.

def account_request(request):
    if request.method == 'POST':

        form = forms.ApiKeysForm(request.POST)
        if form.is_valid():
            key = form.cleaned_data.get('key')
            password = form.cleaned_data.get('password')
            instance = form.save(commit=False)
            instance.user = request.user
            instance.save()
            c = {
                "key": key,
                "password": password
            }

            result = ConnAPI.set_keys(key, password)  # gets the return from the set keys method
            messages.success(request, result)  # a toast to display if the api conn was successful or not
            return redirect("main:homepage")
    else:
        form = forms.ApiKeysForm(request.POST)

    context = {
        "form": form,
    }

    return render(request,
                  "main/account.html",
                  context)

This is my account.html template :

{% extends "main/header.html" %}
{% block content%}

{% if UserApiDetails.keys_exist %}

<div class="row1">
    <h2>Your Api Keys have already been entered.</h2>
    <div class="col s12 m8 14">
        <p></p>
    </div>

</div>

{% else %}

<div class="row2">
    <form class="col s12 " action="/account/" method="POST">
        {% csrf_token %}
        {{form.as_p}}

        <div class="col s12 m6 14">
            <button class="btn" type="submit">Submit Api Keys</button>
        </div>

    </form>
</div>

{% endif %}

{% endblock %}

edit I have managed to save the API details with Unique Users but i'm struggling to ensure that each user enters only 1 set of keys.

When a user who does have API keys associated to their account clicks "account" the form still appears instead of the HTML with div ID of "Row1".

There's a few issues with your code, you're mixing instances and classes:

  • First your keys_exist() function is wrong because you use pk=User in the filter of your query, which doesn't make sense. User is the class user and pk is the primary key of your UserApiDetails instances, so they can never be equal. You need to test against a User instance :

     @classmethod def keys_exist(cls, user): return cls.objects.filter(user=user).exists() 

    Note: It would be cleaner to write this method on your User model, but it seems you haven't customised User and are using the plain Django user model, so in this case this is fine.

  • Second, since you need to pass the user as argument to the function, you can't call it directly in your template. So either add the result to your context (and use that in your template) or create a custom template filter. I'll show the first method. In your view, define your context like this:

     context = {'form': form, 'has_keys': UserApiDetails.keys_exist(request.user)} 

    then in your template you can just check {% if has_keys %} .

I've managed to fix the issue:

I firstly had to change my keys_exist() method in models.py as follows:

def keys_exist(request):  # had to pass request to use request.user in the filter
    if UserApiDetails.objects.filter(user=request.user).exists():  # where foreign key = current logged user
        print("User has API details")
        return True
    else:
        print("User does not have API details")
        return False

I then had to add the following to my account_request() view:

    if models.keys_exist(request): # pass request to use in modely.py
        context = {
            "form": form,
            "exists": True,
        }
    else:
        context = {
            "form": form,
            "exists": False,
        }

Making sure to pass request when calling .keys_exist()

And then finally I changed the if statement in my account.html template:

{% if exists  %}  <!-- Do not use the double braces or == true within the code blocks -->
.
.
.
{% else %}     <!-- ensure this is not elif -->
.
.
.
{% endif %}


The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM