skip to Main Content

In my Django API, I’m able to successfully create admin users as well as normal users. This is the code for my models.

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 

I’m using token authentication and this is my code for auth

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)

AND

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

When I log in using the admin credentials, I’m able to successfully login and also generate a token. But the issue arises when logging in as a normal registered user, it raises the error below.

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

This is my client side code

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),
                        ),
                    ),
                    ),
                ),
                ],
            ),
            ),
        ],
        ),
    );
    }
}

Kindly help me out, I don’t know what I’m doing wrong, tried out all solutions on the internet, but I couldn’t solve it.

2

Answers


  1. Chosen as BEST ANSWER

    For those that might encounter similar bug in the future. I was able to solve this by manually deleting my sqlite db, and the running migrations


  2. The error you’re encountering seems to be related to the Token object creation in your Django API. Specifically, it mentions a FOREIGN KEY constraint failure. This usually occurs when you’re trying to create a Token object for a user, but the user referenced by the foreign key doesn’t exist.

    Here’s a potential issue in your 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
    

    The problem is that you’re trying to find the user using the unique_code field, but you’re not checking if the phone number matches. This can lead to a situation where a user with the correct unique_code is not the one making the request.

    Here’s an updated version:

    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
    

    This ensures that you’re checking both the phone number and the unique code when retrieving the user.

    Additionally, make sure that the Users model has a valid implementation of the str method, as it is being used in your print statements. If not, you can update it like this:

    def __str__(self):
        return self.phone
    

    After making these changes, try logging in with a normal registered user again and see if the issue persists.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search