Django OneToOneField and queryset - django

counter_app:
class Logs(models.Model):
name = models.CharField(max_length=250)
number = models.PositiveIntegerField(default=0)
...
mail_app:
class Mail(models.Model):
subject = models.CharField(max_length=250)
counter = models.OneToOneField(Logs, on_delete=models.CASCADE)
...
def save(self, *args, **kwargs):
c = Logs(name=self.subject, number=0)
c.save()
self.counter = c
super(Mail, self).save()
How can I get access to the "c" object? How should I connect it with 'counter' field? And also I need it's 'number' field to be used further.
Many thanks in advance!

Related

Set a field value within form __init__ function

I am trying to find out an efficient way to set a field value within form init method. My models are similar to below
class Users(models.Model):
firstname = models.CharField()
lastname = models.CharField()
class profile(models.model):
user = models.ForeignKey(Users, on_delete=models.PROTECT)
class logindetails(models.model):
user = models.ForeignKey(Users, on_delete=models.PROTECT)
profile = models.ForeignKey(profile, on_delete=models.PROTECT)
login_time = models.DateField(auto_now=True)
My form is like as below:
class LoginForm(forms.ModelForm):
class Meta:
model = logindetails
fields = [__all__]
def __init__(self, *args, **kwargs):
self._rowid = kwargs.pop('rowid', None)
super(LoginForm, self).__init__(*args, **kwargs)
instance = profile.objects.get(id=self._rowid)
self.fields['user'] = instance.user <--- Facing difficulties here
Any help will be appreciated.
Django had built-in ways of setting initial form values, the documentation is available here: https://docs.djangoproject.com/en/3.0/ref/forms/api/#dynamic-initial-values

How to create a save method in an abstract model that checks whether an instance exists?

I have the following models:
class PlaceMixin(models.Model):
name = models.CharField(max_length=200, null=True, blank=True)
address = models.CharField(max_length=200, null=True, blank=True)
sublocality = models.CharField(max_length=100, null=True, blank=True)
city = models.CharField(max_length=100, null=True, blank=True)
class Meta:
abstract = True
class Bar(PlaceMixin):
pass
class Restaurant(PlaceMixin):
pass
Bar and Restaurant have almost same save() method:
def save(self, *args, **kwargs):
try:
bar = Bar.objects.get(address=self.address)
except Bar.DoesNotExist:
Do something
super().save(*args, **kwargs)
def save(self, *args, **kwargs):
try:
restaurant = Restaurant.objects.get(address=self.address)
except Restaurant.DoesNotExist:
Do something
super().save(*args, **kwargs)
I was wondering if I can put the method in the Abstract model and pass it to the two inherited model?
def save(self, *args, **kwargs):
try:
temp = self.objects.get(address=self.address)
except self.DoesNotExist:
Do something
super().save(*args, **kwargs)
Something like this? But you can query in an abstract model. I basically need to check if an instance exists for executing an action.
You can make a common save method for both Restaurant and Bar model in a Mixin class like this:
from django.apps import apps
class CommonMixin(object):
def save(self, *args, **kwargs):
if self.__class__.__name__ == "Resturant":
model = apps.get_model('app_name', 'Bar')
if model.objects.filter(address=self.address).exists():
...
else:
model = apps.get_model('app_name', 'Restaurant')
if model.objects.filter(address=self.address).exists():
...
super(CommonMixin, self).save(*args, **kwargs)
And import it in both Restaurant and Bar class:
class Restaurant(CommonMixin, PlaceMixin):
...
class Bar(CommonMixin, PlaceMixin):
...
Probably a better approach is to use a separate model for Address information. Then you won't need a new Mixin to override save(the approach given above feels like over engineering). So lets say you have a different address model, there you can simply put unique=True to restrict duplicate entries:
class Address(models.Model):
address = models.CharField(max_length=255, unique=True)
class PlaceMixin(models.Model):
address = models.ForeignKey(Address)
...
You can use abstract metadata to achieve this. And if you want to use any variable inside class model, you just need to use self.__class__ like so:
class PlaceMixin(models.Model):
name = models.CharField(max_length=200, null=True, blank=True)
address = models.CharField(max_length=200, null=True, blank=True)
sublocality = models.CharField(max_length=100, null=True, blank=True)
city = models.CharField(max_length=100, null=True, blank=True)
class Meta:
abstract = True
def save(self, *args, **kwargs):
try:
self.__class__.objects.get(address=self.address)
except self.__class__.DoesNotExist:
# Do something
else:
super().save(*args, **kwargs)
class Bar(PlaceMixin):
pass
class Restaurant(PlaceMixin):
pass
There are a lot of code design like this in Django source code, a lot of good practices in their project so give it a try. E.g: a line of code on Django repo

Django model audit mixin

Hello I wanted to know how to create a few fields and convert them into a mixin.
Let's say I have the following.
class Supplier(models.Model):
name = models.CharField(max_length=128)
created_by = models.ForeignKey(get_user_model(), related_name='%(class)s_created_by')
modified_by = models.ForeignKey(get_user_model(), related_name='%(class)s_modified_by')
created_date = models.DateTimeField(editable=False)
modified_date = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created_date = timezone.now()
self.modified_date = timezone.now()
return super(Supplier, self).save(*args, **kwargs)
I want to create a mixin to avoid writing every time the last 4 fields into different models.
Here is the mixin I would create:
class AuditMixin(models.Model):
created_by = models.ForeignKey(get_user_model(), related_name='%(class)s_created_by')
modified_by = models.ForeignKey(get_user_model(), related_name='%(class)s_modified_by')
created_date = models.DateTimeField(editable=False)
modified_date = models.DateTimeField()
def save(self, *args, **kwargs):
if not self.id:
self.created_date = timezone.now()
self.modified_date = timezone.now()
return super(Supplier, self).save(*args, **kwargs)
class Supplier(AuditMixin):
name = models.Charfield(max_length=128)
How can I make sure that the related_name is relevant to the class the mixin is included into? Also in the save function, How can I make sure the class the mixin is included into is returned (as per the last line)?
Thank you!
Firstly, in any super call, you must always use the current class. So it will always be super(AuditMixin, self)... and your question does not apply.
Django itself takes care of substituting the current class name in related_name if you use the %(class)s syntax, which you have, so again there is nothing else for you to do. See the model inheritance docs.

Django model - set default charfield in lowercase

How to set default charfield in lowercase?
This is my model:
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
password = models.CharField(max_length=64)
name = models.CharField(max_length=200)
phone = models.CharField(max_length=20)
email = models.CharField(max_length=200)
def __init__(self, *args, **kwargs):
self.username = self.username.lower()
I tried the __init__ but it doesn't work. I want to make the username in lowercase every time new record saved. Thanks.
While overwriting save() method is a valid solution. I found it useful to deal with this on a Field level as opposed to the Model level by overwriting get_prep_value() method.
This way if you ever want to reuse this field in a different model, you can adopt the same consistent strategy. Also the logic is separated from the save method, which you may also want to overwrite for different purposes.
For this case you would do this:
class NameField(models.CharField):
def get_prep_value(self, value):
return str(value).lower()
class User(models.Model):
username = models.CharField(max_length=100, unique=True)
password = models.CharField(max_length=64)
name = NameField(max_length=200)
phone = models.CharField(max_length=20)
email = models.CharField(max_length=200)
Just do it in the save method. ie, override the save method of Model class.
def save(self, *args, **kwargs):
self.username = self.username.lower()
return super(User, self).save(*args, **kwargs)
signals also works
from django.db.models.signals import pre_save
#receiver(pre_save, sender=YourModel)
def to_lower(sender, instance=None, **kwargs):
instance.text = instance.text.lower() if \
isinstance(instance.text, str) else ''
In my case I had a recipient_name field that I needed to make all lower case when it is stored on DB
class LowerField(models.CharField):
def get_prep_value(self, value):
return str(value).lower()
class Recipients(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='recipients', on_delete=models.CASCADE, )
recipient_account_number = models.IntegerField()
recipient_name = LowerField(max_length=30)
recipient_bank_name = models.CharField(max_length=30)
date = models.DateTimeField(auto_now=True, verbose_name='Transaction Date')
class Meta:
ordering = ['-date']
def __str__(self):
return self.recipient_name
def get_absolute_url(self):
return reverse('recipient-detail', kwargs={'pk': self.pk})
Similarly, you can apply to another table called Transactions in your app, like this
class Transactions(models.Model):
transaction_type = (
('transfer', 'Transfer'),
)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='transactions', on_delete=models.CASCADE, )
bank_name = LowerField(max_length=50)
def save(self, force_insert=False, force_update=False):
self.YourFildName = self.YourFildName.upper()
super(YourFomrName, self).save(force_insert, force_update)

How create new object automatically after creating other object in django?

For example, I have two classes.
class Book(models.Model):
title = models.CharField (...)
author = models.CharField (...)
price = models.Integer(...)
class Order(models.Model)
order_datetime = models.DateTimeField()
order_book = models.ForeignKey(Book,....)
New Order object should be created automatically after each addition of a new Book object in the database. And order_book fielt should be autocomplited with correspending book object.
How can I make it?
Thanx for the help!
You can override the save method of Book:
class Book(models.Model):
title = models.CharField (...)
author = models.CharField (...)
price = models.Integer(...)
def save(self, *args, **kwargs):
is_new = True if not self.id else False
super(Book, self).save(*args, **kwargs)
if is_new:
order = Order(order_book=self)
order.save()
You can also add auto_now_add=True to order_datetime if it's supposed to be filled with insertion time:
order_datetime = models.DateTimeField(auto_now_add=True)