1

i have some problems with a web im creating. The paid is made and thats work fine, my problem is with only one line of code XD. Im trying to "save" the names of products purchased (in this case is for courses), in a billing and then pass that course to a user-profile so he can gain access to the course. Im very new in python and django, and i know how to do this.

first a big resume of my organization in the project:

myApp  _ apps ______content
      |       |
      |       |_____shopping_cart
      |
      |
      |_ paypal
      |
      |_ user

ok so my model in content is:

class Course(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    thumbnail = models.ImageField(upload_to="courseThumbnails/")
    publish_date = models.DateTimeField(auto_now_add=True)
    last_update = models.DateTimeField(auto_now=True)
    description = models.TextField()
    price = models.FloatField()
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    #agregar despues un field de recursos para descargar material nose si aca o en video mejor

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse("content_app:course-detail", kwargs={"slug":self.slug})

In shopping_ cart model is:

from django.conf import settings
from django.db.models import Sum
from django.db import models
from apps.content.models import Course
# Create your models here.
 
class OrderItem(models.Model):
    course = models.ForeignKey(Course, on_delete=models.CASCADE)

    def __str__(self):
        return self.course.name

class Order(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete= models.CASCADE)
    is_ordered = models.BooleanField(default=False)
    items = models.ManyToManyField(OrderItem)
    ref_code = models.CharField(max_length=50)

    def __str__(self):
        return self.user.username

    
    def get_total(self):
        return self.items.all().aggregate(order_total=Sum('course__price'))['order_total']

and my model in paypal is:

from django.db import models
from apps.shopping_cart.models import Order
from apps.content.models import Course, 

class Compra(models.Model):
    id = models.CharField(primary_key= True, max_length=100)
    estado = models.CharField(max_length=100)
    codigo_estado = models.CharField(max_length=100)
    cursos = models.ManyToManyField(to=Order, related_name='comprado')
    total_de_la_compra = models.DecimalField(max_digits=5 ,decimal_places= 2)
    nombre_cliente = models.CharField(max_length=100)
    apellido_cliente = models.CharField(max_length=100)
    correo_cliente = models.EmailField(max_length=100)
    direccion_cliente = models.CharField(max_length=100)

    class Meta:
        verbose_name = 'Facturacion'
        verbose_name_plural= 'Facturaciones'

    def __str__(self):
        return str(self.nombre_cliente)


class Payment(models.Model):
    order = models.ForeignKey(Order, on_delete=models.CASCADE)
    total_amount = models.FloatField()
    date_paid = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return str(self.order.user)

last model is for user:

from django.db import models
from django.conf import settings
from django.db.models.signals import post_save
from apps.content.models import Course, 

class UserProfile(models.Model):
    cursos = models.ManyToManyField(Course, blank=True)
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

    def __str__(self):
        return self.user.username

    def cursos_list(self):
        return self.cursos.all()


def post_user_signup_receiver(sender, instance, created, *args, **kwargs):
    if created:
        UserProfile.objects.get_or_create(user=instance)
post_save.connect(post_user_signup_receiver, sender=settings.AUTH_USER_MODEL)

Okay with thats model and following a lot of tutorials of paypal (because the oficial documentations it wasnt clear enogh for me) i made a view in paypal app, and here is where the problem is:

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, JsonResponse
from paypalcheckoutsdk.core import PayPalHttpClient, SandboxEnvironment
from paypalcheckoutsdk.orders import OrdersGetRequest, OrdersCaptureRequest
from apps.shopping_cart.models import Order
from user.models import UserProfile
from .models import Compra, Payment

import sys, json


def pago(request):
    order = get_object_or_404(Order, user=request.user)
    payment = Payment()
    payment.order = order
    payment.total_amount = float(order.get_total())
    payment.save()
    print('payment:' + str(payment.total_amount))
   
    data = json.loads(request.body)
    order_id = data['orderID']
    print('order_id: ' + order_id)

    detalle = GetOrder().get_order(order_id) 
    detalle_precio = float(detalle.result.purchase_units[0].amount.value)
   
    if detalle_precio == payment.total_amount:
        trx = CaptureOrder().capture_order(order_id, debug=True)
        print('transaccion: ' + trx.result.id)
       
        pedido = Compra(
            id= trx.result.id,
            estado= trx.result.status,
            codigo_estado= trx.status_code,

            #HERE IS MY PROBLEM
     ---->  **cursos= Order.items.all(),**   <----
     
            total_de_la_compra = trx.result.purchase_units[0].payments.captures[0].amount.value,
            nombre_cliente= trx.result.payer.name.given_name,
            apellido_cliente= trx.result.payer.name.surname,
            correo_cliente= trx.result.payer.email_address,
            direccion_cliente= trx.result.purchase_units[0].shipping.address.address_line_1)
        pedido.save() #guardamos en base de datos la info de la Transaccion
        #la funcion json en el html esta pidiendo una respuesta por lo q se le pasa esta variable data:
        data = {
            "id": f"{trx.result.id}",
            "nombre_cliente": f"{trx.result.payer.name.given_name}",
            "mensaje": "Ahora ya puedes acceder al contenido! =D"
        }
        return JsonResponse(data)
    else:
        data = {
            "mensaje": "Error =( "
        }
        return JsonResponse(data)

if i use the expression in def pago(request): ... cursos = Order.items.all() it prints: TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use cursos.set() instead.

and all other options i dont help me to see in def pago(request) the names of the courses of OrderItems. I want this to made a "bid". And then use it with some sort of for (like this: for curso in cursos: # request.user.UserProfile.cursos.add(curso)) to add it in a UserProfile.

PLEASE HELP ME!!

EDIT: full error in console(have some extras prints()):

System check identified no issues (0 silenced).
October 25, 2020 - 01:10:01
Django version 3.1.2, using settings 'AutismoCDV.settings.local'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[25/Oct/2020 01:10:04] "GET /order-summary/ HTTP/1.1" 200 10001
payment:35.0
order_id: 504320196R962682N
detalle_precio: 35.0
Status Code:  201
Status:  COMPLETED
Order ID:  504320196R962682N
Links: 
    self: https://api.sandbox.paypal.com/v2/checkout/orders/504320196R962682N   Call Type: GET
Capture Ids: 
     9F055159V4840413P
Buyer:
transaccion: 504320196R962682N
Internal Server Error: /pago/
Traceback (most recent call last):
  File "/home/yeti/Documentos/PROG/partesONG/10.03agregaraprofile/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/home/yeti/Documentos/PROG/partesONG/10.03agregaraprofile/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/yeti/Documentos/PROG/partesONG/10.03agregaraprofile/AutismoCDV/paypal/views.py", line 45, in pago
    pedido = Compra(
  File "/home/yeti/Documentos/PROG/partesONG/10.03agregaraprofile/env/lib/python3.8/site-packages/django/db/models/base.py", line 496, in __init__
    _setattr(self, prop, kwargs[prop])
  File "/home/yeti/Documentos/PROG/partesONG/10.03agregaraprofile/env/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 545, in __set__
    raise TypeError(
TypeError: Direct assignment to the forward side of a many-to-many set is prohibited. Use cursos.set() instead.
[25/Oct/2020 01:10:29] "POST /pago/ HTTP/1.1" 500 78941

  • Please add the full error traceback to your question. – Klaus D. Oct 25 '20 at 02:27
  • added the full erorr – marco gonzalez Oct 25 '20 at 07:22
  • You can add items to an many-to-many relationship only it the object you are adding it to has been saved. Remove the assignment from constructor and add it after `save()`. – Klaus D. Oct 25 '20 at 07:28
  • thanks for the fast response. I dont get very well what you mean, are you saying to get order.items.all() out of the function "pago" and then use save()?. If thats not the case can you show me in the function what to do? Thanks – marco gonzalez Oct 25 '20 at 08:05

1 Answers1

0

The error message is very clear. You can easily also google the solution.

What I believe you need to do to remove the error is to remove line

cursos= Order.items.all()

and add

cursos.set(Order.objects.all())

after the line where you save the object. What I think you really want to do is make a relation only to the one order that you are saving and this would be done as follows:

cursos.set(Order.objects.filter(id=order_id))

There is an answer to the same problem also explained here:

Direct assignment to the forward side of a many-to-many set is prohibited. Use emails_for_help.set() instead

Najiva
  • 142
  • 8