【问题标题】:Django model with foreign key throws error on save带有外键的 Django 模型在保存时抛出错误
【发布时间】:2021-03-31 00:50:01
【问题描述】:

我有一个带有外键的模型,当我尝试创建新对象时会引发错误。我相信外键字段(user_profile_id)出于某种原因是NULL(它不应该是),并且在尝试创建时,会抛出错误(该字段永远不应该为null)。这是我得到的错误:

IntegrityError at /api/sensors/

NOT NULL constraint failed: sensors_api_sensor.user_profile_id

Request Method:     POST
Request URL:    http://127.0.0.1:8000/api/sensors/
Django Version:     2.2
Exception Type:     IntegrityError
Exception Value:    

NOT NULL constraint failed: sensors_api_sensor.user_profile_id

Exception Location:     /home/vagrant/env/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py in execute, line 383
Python Executable:  /home/vagrant/env/bin/python3
Python Version:     3.6.9
Python Path:    

['/vagrant',
 '/usr/lib/python36.zip',
 '/usr/lib/python3.6',
 '/usr/lib/python3.6/lib-dynload',
 '/home/vagrant/env/lib/python3.6/site-packages']

Server time:    Mon, 21 Dec 2020 08:29:01 +0000

models.py:

class Sensor(models.Model):
"""Database model for users' sensors"""

user_profile = models.ForeignKey(
    settings.AUTH_USER_MODEL, # first argument is the remote model for this foreign key
    on_delete=models.CASCADE  # if ForeignKey is deleted, delete all associations
)
name = models.CharField(max_length=255)
sensor_type = models.CharField(max_length=255)
surrounding = models.CharField(max_length=255) 
latitude = models.FloatField()
longitude = models.FloatField()
created_at = models.DateTimeField(auto_now_add=True, null=False) # auto adds creation timestamp UTC for each object
updated_at = models.DateTimeField(auto_now=True, null=False)



# other fields required
REQUIRED_FIELDS = ['user_profile','name','type','surrounding']

# model to string conversion method
def __str__(self):
    """Return the model as a string"""
    return self.name # returns the sensor name

序列化器.py

class SensorSerializer(serializers.ModelSerializer):
"""Serializes a sensor object"""

class Meta:
    # points serializer to WeatherSensor model
    model = models.Sensor
    # list fields you want to make accessible from WeatherSensor model in tuple
    fields = ('id', 'user_profile', 'name', 'sensor_type', 'surrounding',
              'latitude', 'longitude', 'created_at', 'updated_at')
    # make foreign key read only by using extra keyword args variable
    extra_kwargs = {'user_profile': {'read_only': True}}

views.py

class SensorListApiView(APIView):
"""Sensor List API View for list and create"""
serializer_class = serializers.SensorSerializer

def post(self, request):
    """Create a weather sensor"""
    # self.serializer_class comes with APIView that retrieves serializer class for our review
    serializer = self.serializer_class(data=request.data)
    # validate input as per serializer class spec
    if serializer.is_valid():
        # can retrieve any field that is defined in our serializer
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    else:
        # use status library to pass human-readable error
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

settings.py

...
# Model Overrides
AUTH_USER_MODEL = 'profiles_api.UserProfile'

在测试我观察到的一些奇怪的事情时:

  1. 在浏览器中使用 authtoken 的 modheader 进行本地测试时,提交时出现上述错误
  2. 在 Django Shell 中,序列化程序是有效的,但是当我保存序列化程序时出现上述错误
  3. 在 Django Shell 中,我可以使用以下代码成功创建对象:
user = models.UserProfile.objects.get(id=1)
ws = models.Sensor.objects.create(
    user_profile=user, name='MoistureSensor1', 
    sensor_type='MoistureSensor', surrounding='greenhouse',
    latitude=37.7749, longitude=122.4194, 
    created_at=timezone.now(), updated_at=timezone.now()
)
ws.save()

【问题讨论】:

  • 'user_profile' 在您的序列化程序中是只读的,并且在您的模型中是必需的...所以除非您通过覆盖 create() 或 save() 自己设置为 request.user(或其他用户),否则此永远不会工作!
  • 我希望将 user_profile 设置为经过身份验证的用户(因此用户无法在其他用户下创建对象),我相信我通过在序列化程序中将字段设置为 read_only 来做到这一点。如果这样做,我还需要在序列化程序中覆盖 create() 吗?
  • 我已经用代码写了一个答案。

标签: python django django-models django-views foreign-keys


【解决方案1】:

尝试从序列化程序中删除user_profile,然后:

if serializer.is_valid():
    serializer.save(user_profile=request.user)

【讨论】:

    【解决方案2】:

    您想使用序列化程序创建与 request.user 相关的对象。

    你需要在这里做的很简单:

    1. 重写 create() 方法。
    2. 使用您的序列化程序上下文在 create() 方法中从视图中获取 request.user。
    # serializers.py
    
    class SensorSerializer(serializers.ModelSerializer):
    """Serializes a sensor object"""
    
        class Meta:
            # points serializer to WeatherSensor model
            model = models.Sensor
            # list fields you want to make accessible from WeatherSensor model in tuple
            fields = ('id', 'user_profile', 'name', 'sensor_type', 'surrounding',
                  'latitude', 'longitude', 'created_at', 'updated_at')
            # make foreign key read only by using extra keyword args variable
            extra_kwargs = {'user_profile': {'read_only': True}}
        
        def create(self, validated_data):
            user = self.context["request"].user
            sensor = models.Sensor.objects.create(user_profile=user, **validated_data)
            return sensor
    
    
    # views.py
    
    class SensorListApiView(APIView):
        """Sensor List API View for list and create"""
        serializer_class = serializers.SensorSerializer
    
        def post(self, request):
            """Create a weather sensor"""
            # self.serializer_class comes with APIView that retrieves serializer class for our review
            serializer = self.serializer_class(data=request.data, context={"request":request})
            # You need to pass the request in the context.
            # etc...
    
    

    请小心,因为我在您的代码中没有看到您检查用户是否实际登录的任何地方。

    【讨论】:

      猜你喜欢
      • 2019-07-27
      • 1970-01-01
      • 2021-09-27
      • 2013-07-07
      • 1970-01-01
      • 2015-06-11
      • 2014-07-13
      • 2012-08-04
      • 2013-06-26
      相关资源
      最近更新 更多