pythondjangodjango-formsgeoip2

Why does Django raised IntegrityError :null value in column "user_id" violates not-null constraint when a form is committed?


I have a UserSession model that creates a session that uses the IPaddress, and city data to populate the model using Geip2. I set up a form that takes the info that I want the user to enter but get:

**django.db.utils.IntegrityError: null value in column "user_id" violates not-null constraint**

when I submit the form. The object gets created but the information that the form takes doesn't save to the object.

views:

class NewLocationCreateForm(CreateView):
        model = UserSession
        success_url = reverse_lazy('post:index')
        form_class = NewLocationCreateForm



 def form_valid(self, form):
        if form.is_valid():
            roote = Post.objects.filter(owner =self.request.user)
            instance = form.save(commit=False)
            instance.owner = self.request.user
            user_logged_in.send(self.request.user, request=self.request)
        return super(NewLocationCreateForm, self).form_valid(form)


    def get_form_kwargs(self):
        kwargs = super(NewLocationCreateForm, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

The problem seems to arrive at at is:

if form.is_valid():
 return super(NewLocationCreateForm, self).form_valid(form) 

The user session call of the Geoip2 seems to work but it doesn't save the form

forms.py

from .models import  UserSession
from post.models import  Post
from django import forms


class NewLocationCreateForm(forms.ModelForm):

    class Meta:
        model = UserSession
        fields = [
            'g_post',
            'coordinate',
            'place_name'
        ]
    def __init__(self,user=None, *args, **kwargs):
        super(NewLocationCreateForm, self).__init__(*args, **kwargs)
        self.fields['g_post'].queryset = Post.objects.filter(owner=user)

models.py:

from django.conf import settings
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
from django.db.models.signals import pre_save
from analytics.signals import user_logged_in
from analytics.utils import get_client_city_data, get_client_ip
from post.models import Post
User = settings.AUTH_USER_MODEL


class UserSessionManager(models.Manager):
    def create_new(self, user, session_key=None, ip_address=None, city_data=None):
        session_new = self.model()
        session_new.user = user
        session_new.session_key = session_key
        if ip_address is not None:
            session_new.ip_address = ip_address
            if city_data:
                session_new.city_data = city_data
                try:
                    city = city_data['city']
                except:
                    city = None
                session_new.city = city
                try:
                    country = city_data['country_name']
                except:
                    country = None
            session_new.country = country
            session_new.save()
            return session_new
        return None

class UserSession(models.Model):
    g_post            = models.ForeignKey(Post, on_delete=models.CASCADE, null=True)
    user            = models.ForeignKey(User)
    coordinate      = models.CharField(max_length=1000, blank=True)
    place_name      = models.CharField(max_length=250, blank=True)
    location_photo  = models.ImageField(upload_to='location_image', blank=True)
    updated         = models.DateTimeField(auto_now=True, null=True)
    session_key     = models.CharField(max_length=60, null=True, blank=True)
    ip_address      = models.GenericIPAddressField(null=True, blank=True)
    city_data       = models.TextField(null=True, blank=True)
    city            = models.CharField(max_length=120, null=True, blank=True)
    country         = models.CharField(max_length=120, null=True, blank=True)
    active          = models.BooleanField(default=True)
    timestamp       = models.DateTimeField(auto_now_add=True)

    objects = UserSessionManager()

    def __str__(self):
        city = self.city
        country = self.country
        place_name = self.place_name
        if city and country:
            return f"{city}, {country}"
        elif city and not country:
            return f"{city}"
        elif country and not city:
            return f"{country}"
        return self.place_name    

def user_logged_in_receiver(sender, request, *args, **kwargs):
    user = sender
    ip_address = get_client_ip(request)
    city_data = get_client_city_data(ip_address)
    request.session['CITY'] = str(city_data.get('city', 'New York'))
    session_key = request.session.session_key
    UserSession.objects.create_new(
                user=user,
                session_key=session_key,
                ip_address=ip_address,
                city_data=city_data
                )



user_logged_in.connect(user_logged_in_receiver)

Solution

  • Next to the fix by @solarissmoke (changing instance.owner to instance.user) which fixes IntegrityError, you are triggering user_logged_in() signal in the is_valid() method here:

    def form_valid(self, form):
        if form.is_valid():
            roote = Post.objects.filter(owner =self.request.user)
            instance = form.save(commit=False)
            instance.user = self.request.user
            # Here you trigger signal that creates UserSession!
            user_logged_in.send(self.request.user, request=self.request)
        return super(NewLocationCreateForm, self).form_valid(form)
    

    One object is therefore created with the CreateView and another one with this signal. Remove this line in form_valid().

    EDIT: To include data from signal, simply add this to form_valid() like this:

    def form_valid(self, form):
        if form.is_valid():
            roote = Post.objects.filter(owner =self.request.user)
            instance = form.save(commit=False)
            instance.user = self.request.user
            instance.ip_address = get_client_ip(self.request)
            instance.city_data = get_client_city_data(instance.ip_address)
            instance.session_key = self.request.session.session_key
        return super(NewLocationCreateForm, self).form_valid(form)