I have two models in an app called customer. The models are Customer and Account. The logic is one customer can have many accounts. So the auto generated default 'id' column in Customer is Foreign Key in Account. Account also has an auto generated default 'id' column. I have created a function for an account_id field in Account which will be generated on a simple algorithm. customer_id and id from Account will be concatenated as 'customer_id'_'id', (for eg. 1_1). The function will first check the current customer_id of the Account instance created and then check which is the last id for that customer_id. For newly added account, it will capture the id part from account_id (i.e. the part after underscore) and increment it by 1. Then concatenation happens for the same customer_id and id. Following is the code-:
models.py
from asyncio.windows_events import NULL
from django.db import models
from django.db.models.signals import pre_save
from django.contrib.auth.models import AbstractBaseUser
from .utils import set_account_id
class Customer(AbstractBaseUser):
name = models.CharField(max_length=50)
phone = models.BigIntegerField(null=True)
email = models.EmailField(max_length=100, null=True)
org_name = models.CharField(max_length=100, null = True)
org_logo = models.ImageField(upload_to='logos', blank=True)
subscription_start_date = models.DateField()
subscription_end_date = models.DateField()
password = models.CharField(max_length=50, blank=True, null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'email']
def __str__(self):
return self.name
class Account(AbstractBaseUser):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
account_id = models.CharField(max_length=100, default=None, blank=True, null=True)
name = models.CharField(max_length=50)
phone = models.BigIntegerField(null=True)
email = models.EmailField(max_length=100, null=True)
password = models.CharField(max_length=50, blank=True, null=True)
account_type = models.CharField(max_length=50, choices=[
("survey", "SURVEY"),
("sme", "SME"),
("residents", "RESIDENTS")
])
account_subscription_date = models.DateField(null=True)
account_expiry_date = models.DateField(null=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'email']
def __str__(self):
return self.name
def pre_save_set_account_id(sender, instance, *args, **kwargs):
if not instance.account_id:
instance.account_id = set_account_id(instance) #Error here
print(instance.account_id)
pre_save.connect(pre_save_set_account_id, sender=Account)
utils.py
def set_account_id(instance):
Klass = instance.__class__
fk = 'customer_id'
obj = Klass.objects.first()
field_object = Klass._meta.get_field(fk)
fk_value = field_object.value_from_object(obj) #Error here
last_account = Klass.objects.filter(customer_id=fk_value).all().order_by('id').last()
accounts_pk = last_account.id
new_account_int = accounts_pk 1
new_account_id = fk_value '_' new_account_int
return new_account_id
Now when I have entered one record in customer and trying to enter a record in account through the admin panel. When I am filling out the form for account in admin panel, I am leaving the account_id as blank. After clicking on Save, I am getting the following error-:
AttributeError at /admin/customer/account/add/ 'NoneType' object has no attribute 'customer_id'
I have commented "Error here" on the specific lines at which the the error was pointed out by Django debugger. I am new to Django and have tried a lot but couldn't resolve the error. Please help.
CodePudding user response:
Hey i don't know why you need this account_id as db field in first place? If you need it to perform filter, I suggest to use post_save signal like:
def post_save_set_account_id(sender, instance, *args, **kwargs):
if instance.account_id is None:
instance.account_id = f'{instance.customer.id}_{instance.id}'
instance.save()
But if you don't need to build sql query based on account_id field i suggest use @property in Account model:
@property
def account_id(self):
return f'{self.id}_{self.account.id}'
then you cen use it as model field for example:
account = Account.objects.get(pk=1)
account.account_id
prints for example:
1_1