model OneToOneField - Fehler beim Serialisieren

Django, Flask, Bottle, WSGI, CGI…
Antworten
robin_
User
Beiträge: 48
Registriert: Montag 3. August 2020, 17:59

Hi zusammen,

ich habe folgendes Model:

Code: Alles auswählen

class InvoiceAddress(models.Model):
    # task = models.OneToOneField(Task, on_delete=models.CASCADE, related_name="invoice_address")
    task = models.ForeignKey(Task, on_delete=models.CASCADE, related_name="invoice_address")
    inv_company = models.CharField(max_length=250, null=True, blank=True)
    inv_receiver = models.CharField(max_length=250, null=True, blank=True)
    inv_street = models.CharField(max_length=250, null=True, blank=True)
    inv_city = models.CharField(max_length=250, null=True, blank=True)
und dazu (ein Teil) meiner APIView-Subclass:

Code: Alles auswählen

def post(self, request, pk):
        try:
            user = request.user
            task = user.tasks.get(pk=pk)
        except:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        serializer = InvoiceAddressSerializer(data=request.data)
        if serializer.is_valid():
            print("Is valid", task)
            serializer.save(task=task)
        
        return Response(serializer.data)
So wie es hier ist, klappt es. Ich möchte aber das Attribut 'task' als OneToOneField haben, denn jede Instanz von Task soll (maximal) eine Instance vom Typ InvoiceAddress haben.
Und schon klappts nicht mehr :D

Das Problem tritt bei

Code: Alles auswählen

serializer.save(task=task)
auf. Die übergebene Instanz ist definitiv initialisiert. Offenbar bedarf es für ein OneToOneField eine andere herangehensweise als für ein ForeignKey - Feld... Vielleicht könnt ihr mir helfen. Die Konsole zeigt folgendes an:

Code: Alles auswählen

Is Valid!+++++++++++++ Test-Task 2020-10-24
Internal Server Error: /api/45/invoice-address/
Traceback (most recent call last):
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\sqlite3\base.py", line 413, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: crog_invoiceaddress.task_id

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

Traceback (most recent call last):
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\core\handlers\base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\Robin\DEV\crog-project\crog-drf\crog\Task\views.py", line 111, in post
    address = serializer.save(task=task)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\rest_framework\serializers.py", line 205, in save
    self.instance = self.create(validated_data)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\rest_framework\serializers.py", line 939, in create
    instance = ModelClass._default_manager.create(**validated_data)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\query.py", line 447, in create
    obj.save(force_insert=True, using=self.db)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\base.py", line 753, in save
    self.save_base(using=using, force_insert=force_insert,
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\base.py", line 790, in save_base
    updated = self._save_table(
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\base.py", line 895, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\base.py", line 933, in _do_insert
    return manager._insert(
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\query.py", line 1254, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\models\sql\compiler.py", line 1397, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\utils.py", line 98, in execute
    return super().execute(sql, params)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Robin\DEV\crog-project\env\lib\site-packages\django\db\backends\sqlite3\base.py", line 413, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: crog_invoiceaddress.task_id
Mit dem Fehler finde ich leider nicht so viel bei stackoverflow und co :/

Vielen Dank euch & einen schönen Abend :))
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

Wenn du eine OneToOne-Relation erschaffst, dann wird das Feld task_id in "crog_invoiceaddress" unique - also einzigartig. Denn für jeden Task kann es ja nur eine InvoiceAddress geben.
Du versuchst eine weitere InvoiceAddress für einen Task zu erstellen.
robin_
User
Beiträge: 48
Registriert: Montag 3. August 2020, 17:59

Ich hatte eigentlich vor, beim POST die task_id nicht zu übergeben, da die URL den pk bereits liefert. Daher mein Versuch, das entsprechend des Codes oben zuzuweisen.

Also task_id doch mitübergeben oder gibt es eine Lösung dafür?
Benutzeravatar
sparrow
User
Beiträge: 4237
Registriert: Freitag 17. April 2009, 10:28

Nee, du hast das Problem noch nicht verstranden. Das hat nichts damit zu tun, was du wohin übergibst.

Eine OneToOne-Relation sagt: Es darf für jeden Task nur eine InvoiceAddress geben.
Du versuchst aber für einen Task, der bereits eine InvoiceAddress hat, eine _weitere_ InvoiceAddress anzulegen. Das ist dann aber nicht mehr OneToOne.

Edit:

Ungetestet zur Verdeutlichung:

Code: Alles auswählen

def post(self, request, pk):
    try:
        user = request.user
        task = user.tasks.get(pk=pk)
    except:
        return Response(status=status.HTTP_400_BAD_REQUEST)

    serializer = InvoiceAddressSerializer(data=request.data)
    if serializer.is_valid():
        print("Is valid", task)
        address_invoices = AddressInvoice.objects.filter(task=task).all()
        print(f"Already existing AddressInvoices for task {task}: {address_invoices}")
        serializer.save(task=task)
        
    return Response(serializer.data)
PS: Eingerückt wird mit 4 Leerzeichen je Ebene. Nicht mit 8 und auch nicht mit Tabstops.
robin_
User
Beiträge: 48
Registriert: Montag 3. August 2020, 17:59

Ok danke, ich dachte damit würde man einfach das "alte" Obj. überschreiben.

Zu dem Einrücken: Keine Ahnung, warum ich da 8 LZ drin hatte. Ich nutze VS Code und ein Tab wird zu 4 Leerzeichen umgewandelt, das müsste daher eig. passen.
Im Code sind ja auch Leerzeichen anstelle Tabs drin.
Antworten