Django REST API错误:FOREIGN KEY约束失败

yrdbyhpb  于 12个月前  发布在  Go
关注(0)|答案(2)|浏览(114)

在我的Django API中,我能够成功地创建管理员用户和普通用户。这是我的模型的代码。

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models

class CustomUserManager(BaseUserManager):
    def create_user(self, phone, password=None, **extra_fields):
        if not phone:
            raise ValueError('The phone field must be set')
        user = self.model(phone=phone, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, phone, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')
        return self.create_user(phone, password, **extra_fields)

class Users(AbstractBaseUser, PermissionsMixin):
    unique_code = models.TextField()
    fname = models.TextField()
    lname = models.TextField()
    email = models.EmailField(unique=True)  
    phone = models.TextField(unique=True)  
    sex = models.TextField()
    country = models.TextField()
    date_of_birth = models.TextField()
    image = models.TextField(default=None, blank=True, null=True)
    district = models.TextField()
    subCounty = models.TextField()
    village = models.TextField()
    number_of_dependents = models.TextField()
    family_information = models.TextField()
    next_of_kin_name = models.TextField()
    next_of_kin_has_phone_number = models.IntegerField(default=None, blank=True, null=True)
    next_of_kin_phone_number = models.TextField(default=None, blank=True, null=True)
    pwd_type = models.TextField(default=None, blank=True, null=True)
    
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)

    REQUIRED_FIELDS = ['unique_code', 'fname', 'lname', 'phone']  
    USERNAME_FIELD = 'email'  

    objects = CustomUserManager()

    def __str__(self):
        return self.phone

字符串
我使用令牌身份验证,这是我的验证代码

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.response import Response
from rest_framework import status
from digi_save_vsla_api.auth import PhoneCodeBackend
from digi_save_vsla_api.serializers import LoginSerializer
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model
from rest_framework.authtoken.models import Token

@csrf_exempt
def login_with_phone_unique_code(request):
    if request.method == 'POST':
        print('Received POST request data:', request.POST)
        serializer = LoginSerializer(data=request.POST)
        
        if serializer.is_valid():
            phone = serializer.validated_data["phone"]
            code = serializer.validated_data["unique_code"]
            backend = PhoneCodeBackend()
            user = backend.authenticate(request=request, phone=phone, unique_code=code)
            print('User object: ', user)
            print('User phone:', phone)
            print('User code:', code)
            
            if user is not None:
                # user = get_user_model().objects.get(id=user.id)
                token, created = Token.objects.get_or_create(user=user)

                response_data = {
                    "status": status.HTTP_200_OK,
                    'success': True,
                    "Token": token.key if token else None,
                    'user': {
                        'fname': user.fname,
                        'lname': user.lname,
                        'email': user.email,
                        'image': user.image,
                        'unique_code':user.unique_code,
                        'phone': user.phone,
                        'sex': user.sex,
                        'country': user.country,
                        'date_of_birth': user.date_of_birth,
                        'district': user.district,
                        'subCounty': user.subCounty,
                        'village': user.village,
                        'number_of_dependents': user.number_of_dependents,
                        'family_information': user.family_information,
                        'next_of_kin_name': user.next_of_kin_name,
                        'next_of_kin_has_phone_number': user.next_of_kin_has_phone_number,
                        'next_of_kin_phone_number': user.next_of_kin_phone_number,
                        'pwd_type': user.pwd_type,
                    },
                }
                return JsonResponse(response_data, status=status.HTTP_200_OK)
            
            else:
                response = {
                    "status": status.HTTP_401_UNAUTHORIZED,
                    "message": "Invalid Email or Password",
                }
                return JsonResponse(response, status=status.HTTP_401_UNAUTHORIZED)
        
        else:
            response = {
                "status": status.HTTP_400_BAD_REQUEST,
                "message": "Bad request",
                "data": serializer.errors
            }
            return JsonResponse(response, status=status.HTTP_400_BAD_REQUEST)
    
    else:
        response = {
            "status": status.HTTP_405_METHOD_NOT_ALLOWED,
            "message": "Method Not Allowed",
        }
        return JsonResponse(response, status=status.HTTP_405_METHOD_NOT_ALLOWED)


from django.contrib.auth import get_user_model
from rest_framework.exceptions import AuthenticationFailed

from digi_save_vsla_api.models import Users
 

class PhoneCodeBackend:
    def authenticate(self, request, phone=None, unique_code=None):
        try:
            user = Users.objects.get(unique_code=unique_code)
        except Users.DoesNotExist:
            raise AuthenticationFailed('User not found')

        # Assuming your User model has a field 'unique_code'
        if user.phone != phone:
            raise AuthenticationFailed('Invalid phonr number')

        return user

    def get_user(self, user_id):
        try:
            return Users.objects.get(pk=user_id)
        except Users.DoesNotExist:
            return None


当我使用管理员凭据登录时,我能够成功登录并生成令牌。但是当作为普通注册用户登录时出现问题,它引发了下面的错误。

Received POST request data: <QueryDict: {'phone': ['+256701391158'], 'unique_code': ['LGZYBL']}>
User object:  +256701391158
User phone: +256701391158
User code: LGZYBL
Internal Server Error: /login-with-phone-code/
Traceback (most recent call last):
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/models/query.py", line 916, in get_or_create
    return self.get(**kwargs), False
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/models/query.py", line 637, in get
    raise self.model.DoesNotExist(
rest_framework.authtoken.models.Token.DoesNotExist: Token matching query does not exist.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/backends/base/base.py", line 313, in _commit
    return self.connection.commit()
sqlite3.IntegrityError: FOREIGN KEY constraint failed

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
  File "/home/mcrops/Documents/digi_save_api/digi_save_vsla_api/views/auth_view.py", line 28, in login_with_phone_unique_code
    token, created = Token.objects.get_or_create(user=user)
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/models/query.py", line 923, in get_or_create
    return self.create(**params), True
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/transaction.py", line 263, in __exit__
    connection.commit()
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/utils/asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/backends/base/base.py", line 337, in commit
    self._commit()
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/backends/base/base.py", line 313, in _commit
    return self.connection.commit()
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/mcrops/Documents/digi_save_api/env/lib/python3.9/site-packages/django/db/backends/base/base.py", line 313, in _commit
    return self.connection.commit()
django.db.utils.IntegrityError: FOREIGN KEY constraint failed
[15/Nov/2023 10:05:02] "POST /login-with-phone-code/ HTTP/1.1" 500 120682


这是我的客户端代码

import 'package:flutter/material.dart';
import 'package:intl_phone_number_input/intl_phone_number_input.dart';
import 'package:omulimisa_digi_save_v2/database/constants.dart';
import 'package:omulimisa_digi_save_v2/database/getData.dart';
import 'package:omulimisa_digi_save_v2/database/getMeetings.dart';
import 'package:omulimisa_digi_save_v2/database/userData.dart';
import '/src/view/screens/start_screen.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../database/localStorage.dart';
import '../widgets/start_card.dart';
import '../widgets/user_class.dart';
import 'package:connectivity/connectivity.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

class PhoneForm extends StatefulWidget {
    const PhoneForm({Key? key}) : super(key: key);

    @override
    _PhoneFormState createState() => _PhoneFormState();
}

class _PhoneFormState extends State<PhoneForm> {
    final _formKey = GlobalKey<FormState>();
    final _controller = TextEditingController();
    final String _initialCountry = 'UG';
    final PhoneNumber _number = PhoneNumber(isoCode: 'UG');
    final _passwordController = TextEditingController();
    final controller = TextEditingController();
    String? _country;
    String? _phone;
    String? _test;

    Future<void> saveLoginStatus(bool isLoggedIn) async {
    final prefs = await SharedPreferences.getInstance();
    prefs.setBool('isLoggedIn', isLoggedIn);
    }

    DatabaseHelper dbHelper = DatabaseHelper.instance;

    Future<List<Map<String, dynamic>>?> checkData() async {
    final data = dbHelper.getUnsyncedUser();
    return data;
    }

    Future<void> checkLoginStatus() async {
    final prefs = await SharedPreferences.getInstance();
    final isLoggedIn = prefs.getBool('isLoggedIn') ?? false;

    if (isLoggedIn) {
        Navigator.of(context).push(
        MaterialPageRoute(
            builder: (context) => const StartScreen(),
        ),
        );
    }
    }

    Future<void> saveUserData(User user) async {
    final prefs = await SharedPreferences.getInstance();
    prefs.setString('token', user.token);
    prefs.setString('userFirstName', user.firstName);
    prefs.setString('userLastName', user.lastName);
    // prefs.setString('token', user.token!);
    }

    // Retrieve user data from shared preferences
    Future<void> printUserData() async {
    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString('token');
    final userFirstName = prefs.getString('userFirstName');
    final userLastName = prefs.getString('userLastName');

    if (token != null && userFirstName != null && userLastName != null) {
        print('User ID: $token');
        print('User First Name: $userFirstName');
        print('User Last Name: $userLastName');
    } else {
        print('User data not found in shared preferences.');
    }
    }

    void showNoInternetSnackBar(BuildContext context) {
    ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
        content:
            Text('No internet connection. Please check your network settings.'),
        duration: Duration(seconds: 5), // You can adjust the duration as needed
        ),
    );
    }

    @override
    void initState() {
    super.initState();
    checkData();
    checkLoginStatus();
    }

    Future<void> loginUser(String phoneNumber, String pinCode) async {
    DatabaseHelper dbHelper = DatabaseHelper.instance;

    var connectivityResult = await (Connectivity().checkConnectivity());

    if (connectivityResult == ConnectivityResult.none) {
        showNoInternetSnackBar(context); // Show the SnackBar
        return;
    }

    // Perform the login process if internet is available
    final apiUrl = Uri.parse('${ApiConstants.baseUrl}/login-with-phone-code/');
    final headers = {'Content-Type': 'application/json'};
    final body = json.encode({'phone': phoneNumber, 'unique_code': pinCode});
    final Map<String, String> data = {
        'phone': phoneNumber,
        'unique_code': pinCode,
    };
    print('JSON: :$body');
    print('Here');

    final response = await http.post(apiUrl, body: data);

    if (response.statusCode == 200) {
        // Parse the response data
        final Map<String, dynamic> responseData = json.decode(response.body);
        print('Response: $responseData');

        // // Access user data and token from responseData
        // String token = responseData['token'];
        Map<String, dynamic> userData = responseData['user'];
        // getDataGroupWithApi();
        // getDataMeetingWithApi();

        String token = responseData['Token'];
        String code = userData['unique_code'];

        await saveUserData(User(
        token: token,
        firstName: userData['fname'],
        lastName: userData['lname'],
        ));

        syncUserDataWithApi();
        int? userId = await dbHelper.getUserIdFromUniqueCode(code);

        // ignore: use_build_context_synchronously
        ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
            content: Text(
                'Welcome, ${userData['fname']} ${userData['lname']} your token is $token!'),
        ),
        );

        // print('User ID: ${userData['id']}');
        print('First Name: ${userData['fname']}');
        print('Last Name: ${userData['lname']}');

        // String idString = userData['id'].toString();
        // int userId = int.parse(idString);
        print('User token: $token');

        // Store the token securely
        // await storage.write(key: 'token', value: token);
        await saveLoginStatus(true);
        await saveUserData(User(
        id: userId,
        token: token,
        firstName: userData['fname'],
        lastName: userData['lname'],
        ));

        printUserData();

        // Now you can use the token and user information as needed
        // print('Token: $token');
        Navigator.of(context).push(
        MaterialPageRoute(
            builder: (context) => const StartScreen(),
        ),
        );
    } else {
        // Handle errors or display appropriate messages
        print('Failed to log in. Status code: ${response.statusCode}');
        print('Response body: ${response.body}');
    }
    }

    @override
    Widget build(BuildContext context) {
    return Padding(
        padding: const EdgeInsets.all(8),
        child: Column(
        children: [
            const Align(
            alignment: Alignment.center,
            child: StartCard(
                theWidth: 500.0,
                theHeight: 200.0,
                borderRadius: 0,
                theChild: Padding(
                padding: EdgeInsets.all(8.0),
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                    Padding(
                        padding:
                            EdgeInsets.symmetric(horizontal: 16, vertical: 16),
                        child: Text(
                        'DigiSave VSLA Mobile App',
                        style: TextStyle(
                            color: Colors.black,
                            fontSize: 18.0,
                            fontWeight: FontWeight.bold,
                        ),
                        ),
                    ),
                    Padding(
                        padding: EdgeInsets.symmetric(horizontal: 16),
                        child: Text(
                        'Enter your phone number and pin to login',
                        style: TextStyle(
                            fontSize: 14,
                            color: Color.fromARGB(255, 0, 20, 1),
                            fontWeight: FontWeight.w500,
                        ),
                        ),
                    ),
                    ],
                ),
                ),
            ),
            ),
            Form(
            key: _formKey,
            child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                Card(
                    elevation: 4,
                    margin: const EdgeInsets.symmetric(horizontal: 16),
                    child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: InternationalPhoneNumberInput(
                        textStyle: const TextStyle(
                        color: Colors.black,
                        ),
                        onInputChanged: (PhoneNumber number) {
                        setState(() {
                            _phone = number.phoneNumber;
                            _country = number.isoCode!;
                        });
                        },
                        onSaved: (PhoneNumber? number) {
                        if (number != null) {
                            print('Phone Number Saved: ${number.phoneNumber}');
                            _test = number.phoneNumber;
                        }
                        },
                        selectorConfig: const SelectorConfig(
                        selectorType: PhoneInputSelectorType.BOTTOM_SHEET,
                        ),
                        ignoreBlank: false,
                        autoValidateMode: AutovalidateMode.disabled,
                        selectorTextStyle: const TextStyle(color: Colors.black),
                        initialValue: _number,
                        textFieldController: controller,
                        formatInput: true,
                        keyboardType: const TextInputType.numberWithOptions(
                        signed: true,
                        decimal: true,
                        ),
                        inputDecoration: const InputDecoration(
                        enabledBorder: OutlineInputBorder(
                            borderSide:
                                BorderSide(color: Colors.green, width: 2.0),
                        ),
                        focusedBorder: OutlineInputBorder(
                            borderSide:
                                BorderSide(color: Colors.black, width: 2.0),
                        ),
                        ),
                    ),
                    ),
                ),
                const SizedBox(
                    height: 15,
                ),
                Padding(
                    padding: const EdgeInsets.all(16),
                    child: Container(
                    decoration: BoxDecoration(
                        boxShadow: [
                        BoxShadow(
                            color: Colors.grey.withOpacity(0.5),
                            spreadRadius: 5,
                            blurRadius: 7,
                            offset: const Offset(0, 3),
                        ),
                        ],
                    ),
                    child: TextFormField(
                        controller: _passwordController,
                        decoration: const InputDecoration(
                        labelText: 'Enter Pin',
                        enabledBorder: OutlineInputBorder(
                            borderSide: BorderSide(color: Colors.white),
                        ),
                        focusedBorder: OutlineInputBorder(
                            borderSide: BorderSide(color: Colors.white),
                        ),
                        filled: true,
                        fillColor: Colors.white,
                        labelStyle: TextStyle(
                            color: Color.fromARGB(255, 82, 80, 80),
                            fontWeight: FontWeight.bold,
                            fontSize: 14,
                        ),
                        ),
                        obscureText: true,
                        validator: (value) {
                        if (value == null || value.isEmpty) {
                            return 'Please enter your pin';
                        }
                        return null;
                        },
                    ),
                    ),
                ),
                const SizedBox(height: 20),
                Center(
                    child: ElevatedButton(
                    onPressed: () async {
                        if (_formKey.currentState!.validate()) {
                        _formKey.currentState!.save();
                        print('Phone number: $_test');
                        String uniqueCode = _passwordController.text;
                        print('Full Typed phone is: $_test');
                        if (_test != null) {
                            loginUser(_test!, uniqueCode);
                        } else {
                            ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                                content: Text('Phone number is required.'),
                            ),
                            );
                        }
                        }
                    },
                    style: TextButton.styleFrom(
                        foregroundColor: Colors.black,
                        backgroundColor: const Color.fromARGB(255, 1, 67, 3),
                        shape: RoundedRectangleBorder(
                            borderRadius: BorderRadius.circular(10.0))),
                    child: const Padding(
                        padding: EdgeInsets.symmetric(
                            horizontal: 20.0, vertical: 12.0),
                        child: Text(
                        'Login',
                        style: TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 14.0,
                            color: Colors.white),
                        ),
                    ),
                    ),
                ),
                ],
            ),
            ),
        ],
        ),
    );
    }
}


请帮帮我,我不知道我做错了什么,尝试了互联网上的所有解决方案,但我不能解决它。

wswtfjt7

wswtfjt71#

您遇到的错误似乎与Django API中的Token对象创建有关。具体来说,它提到了FOREIGN KEY约束失败。这通常发生在您尝试为用户创建Token对象时,但外键引用的用户不存在。
这是你的Django PhoneCodeBackend中的一个潜在问题:

def authenticate(self, request, phone=None, unique_code=None):
    try:
        user = Users.objects.get(unique_code=unique_code)
    except Users.DoesNotExist:
        raise AuthenticationFailed('User not found')

    # Assuming your User model has a field 'unique_code'
    if user.phone != phone:
        raise AuthenticationFailed('Invalid phone number')

    return user

字符串
问题是,您试图使用unique_code字段查找用户,但没有检查电话号码是否匹配。这可能导致具有正确unique_code的用户不是发出请求的用户的情况。
以下是更新版本:

def authenticate(self, request, phone=None, unique_code=None):
    try:
        user = Users.objects.get(phone=phone, unique_code=unique_code)
    except Users.DoesNotExist:
        raise AuthenticationFailed('User not found')

    return user


这样可以确保在检索用户时同时检查电话号码和唯一代码。
此外,确保Users模型具有str方法的有效实现,因为它正在您的print语句中使用。如果没有,您可以像这样更新它:

def __str__(self):
    return self.phone


进行这些更改后,请再次尝试使用普通注册用户登录,看看问题是否仍然存在。

xmjla07d

xmjla07d2#

对于那些可能会遇到类似的错误在未来。我能够解决这个问题,手动删除我的sqlite数据库,并运行迁移

相关问题