Django: cliente con varias direcciones, solo puede tener una dirección principal

CorePress2024-01-24  9

Creé una aplicación 'customerbin' dentro de mi instalación de Django. Necesito crear/editar/eliminar clientes que puedan tener varias direcciones donde solo una dirección puede ser principal. Si se elimina un cliente, también se deben eliminar todas las direcciones que pertenecen a ese cliente. Si se crea un nuevo cliente, no podemos elegir una dirección de otro cliente.

modelos.py:

from django.db import models

class Address(models.Model):
    street = models.CharField(max_length=100)
    number = models.IntegerField(null=True)
    postal = models.IntegerField(null=True)
    city = models.CharField(max_length=100)
    country = models.CharField(max_length=100)
    is_primary = models.BooleanField(null=False)
    geo_lat = models.DecimalField(max_digits=22, decimal_places=16, blank=True, null=True)
    geo_lon = models.DecimalField(max_digits=22, decimal_places=16, blank=True, null=True)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

class Customer(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    vat = models.CharField(max_length=100)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)

    address = models.ForeignKey(Address, on_delete=models.CASCADE)

admin.py:

from django.contrib import admin
from . import models

# Register your models here.

admin.site.register(models.Customer)
admin.site.register(models.Address)

¿Cómo puedo hacer que:

¿Una dirección está vinculada exclusivamente a un cliente? ¿Se eliminan todas las direcciones cuando elimino un cliente? ¿Solo una dirección es principal para un cliente?

------------------------------------

En lugar de unClave externa del cliente a la dirección debe tener una clave externa de la dirección al cliente. Para garantizar que solo exista una dirección principal para un usuario, utilice UniqueConstraint [Django-docs] con una condición:

from django.db.models import Q


class Address(models.Model):
    street = models.CharField(max_length=100)
    number = models.IntegerField(null=True)
    postal = models.IntegerField(null=True)
    city = models.CharField(max_length=100)
    country = models.CharField(max_length=100)
    is_primary = models.BooleanField(null=False)
    geo_lat = models.DecimalField(max_digits=22, decimal_places=16, blank=True, null=True)
    geo_lon = models.DecimalField(max_digits=22, decimal_places=16, blank=True, null=True)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)
    # Here ↓
    customer = models.ForeignKey("Customer", on_delete=models.CASCADE, related_name="addresses")
    
    def save(self, *args, **kwargs):
        if self.is_primary:
            self.__class__._default_manager.filter(customer=self.customer, is_primary=True).update(is_primary=False)
        super().save(*args, **kwargs)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['customer'],
                condition=Q(is_primary=True),
                name='unique_primary_per_customer'
            )
        ]


class Customer(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    vat = models.CharField(max_length=100)
    created_on = models.DateTimeField(auto_now_add=True)
    updated_on = models.DateTimeField(auto_now=True)
    
    # Remove address from here

Aquí on_delete=models.CASCADE provocaría que se elimine una dirección si se elimina el cliente relacionado.

2

Esto parece hacer lo que espero que haga, excepto cuando edito una dirección y hago que una dirección no principal sea principal. Luego arroja un error: 'Error en la restricción ÚNICA: customerbin_address.customer_id'

-andyderuyter

27/03/2021 a las 11:05

@andyderuyter Tienes que encargarte de ese caso. Edité mi respuesta y anulé el método de guardar del modelo para hacerlo (configuraré todas las demás direcciones del cliente como no la dirección principal). Sin embargo, tenga en cuenta que esto tiene algunas advertencias, por ejemplo, Bulk_create o Bulk_update no usarían el método de guardar, por lo que le darían el error que acaba de recibir (si un cliente ya tiene una dirección principal).

- Abdul Aziz Barkat

27 de marzo de 2021 a las 11:00:12



------------------------------------

En primer lugar, estás vinculando los dos modelos incorrectamente. Según el código anterior, cuando se elimina la dirección correspondiente, se elimina el usuario, que no es lo que usted desea. Entonces, aquí está mi sugerencia. Agregue una clave externa en el modelo de Dirección al modelo de Cliente con on_delete=models.CASCADE también agregue un campo booleano is_primary en el modelo de Dirección con un valor predeterminado de Falso. Y puede manejar la lógica para una sola dirección principal en el método limpio del modelo de dirección, donde puede verificar si ya existe una dirección principal para el usuario cuya dirección se está agregando. En caso afirmativo, cree un error de validación.o. Si no, continúa como de costumbre. Si esta explicación no es suficiente, también puedo ayudarte con el código.

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare