how to override the update method of a model in django - python

I am using django with python. I am trying to update the model whenever a field is updated, in this case because i have a lambda function in the cloud, i want when a postgres query update an instance of the model, during the update action, update the age of the Person model below:
data
Contact table
id = 1
name = 'john'
age = 38
sql
UPDATE contacts_contact SET name = 'jane' where id = '1'; # this works fine
now i want to make sure that when the name is changed to jane as above, that the age update automatically in django with the override method
django
class Contact(models.Model):
..
name = models.CharField()
age = models.IntegerField()
def update(self, *args, **kwargs):
if self.age:
self.age = 25
super().update(*args, **kwargs) # i tried this
super(Contact, self).update(*args, **kwargs) # i tried this too
both update methods i tried above do not update the age of the person regardless of the fact that the sql query update worked
is there something that i am missing?
PS: I want to update that field specifically in django, not in the sql query

"I am using django with python. I am trying to update the model whenever a field is updated" if this is the case django signals can help you, in-fact they are built for this purpose only.
follow the below to steps to enable the django-signals for your models.
Inside your_app/app.py you can find the snippets as below. if not paste the same and modify it with your app name. here you're just importing the signals(contents in your_app/signal.py)
from django.apps import AppConfig
class YourAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'your_app'
verbose_name = 'Your App'
def ready(self):
import your_app.signals
Inside your_app/signals.py paste the contents below in it. modify it with your own model name. for now I'll use your Contact model.
from django.db.models.signals import post_save
from your_app.models import Contact
from django.dispatch import receiver
#receiver(post_save, sender=Contact)
def create_user(sender, instance, created, **kwargs):
print(sender)
print(instance)
print(kwargs)
if created:
print('Hurray its created', created)
here you can use post_save signal. Django includes a “signal dispatcher” which helps decoupled applications get notified when actions occur elsewhere in the framework. In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.
whenever you call .save() method of Contact model(create/update). for example -
c = Contact(name='zzz', age=20)
c.save()
now this will make the post_save signal to get notified about the changes, you can verify the same in your create_user() method print statements. from here you can do whatever you want with your model.
you can refer more about post_save signals here https://docs.djangoproject.com/en/4.0/ref/signals/#post-save

Related

How to map an existing python class to a Django model

I'm writing a web scraper to get information about customers and appointment times to visit them. I have a class called Job that stores all the details about a specific job. (Some of its attributes are custom classes too e.g Client).
class Job:
def __init__(self, id_=None, client=Client(None), appointment=Appointment(address=Address(None)), folder=None,
notes=None, specific_reqs=None, system_notes=None):
self.id = id_
self.client = client
self.appointment = appointment
self.notes = notes
self.folder = folder
self.specific_reqs = specific_reqs
self.system_notes = system_notes
def set_appointment_date(self, time, time_format):
pass
def set_appointment_address(self, address, postcode):
pass
def __str__(self):
pass
My scraper works great as a stand alone app producing one instance of Job for each page of data scraped.
I now want to save these instances to a Django database.
I know I need to create a model to map the Job class onto but that's where I get lost.
From the Django docs (https://docs.djangoproject.com/en2.1/howto/custom-model-fields/) it says in order to use my Job class in the Django model I don't have to change it at all. That's great - just what I want. but I can't follow how to create a model that maps to my Job class.
Should it be something like
from django.db import models
import Job ,Client
class JobField(models.Field):
description = "Job details"
def __init__(self, *args, **kwargs):
kwargs['id_'] = Job.id_
kwargs['client'] = Client(name=name)
...
super().__init__(*args, **kwargs)
class Job(models.Model):
job = JobField()
And then I'd create a job using something like
Job.objects.create(id_=10101, name="Joe bloggs")
What I really want to know is am I on the right lines? Or (more likely) how wrong is this approach?
I know there must be a big chunk of something missing here but I can't work out what.
By mapping I'm assuming you want to automatically generate a Django model that can be migrated in the database, and theoretically that is possible if you know what field types you have, and from that code you don't really have that information.
What you need to do is to define a Django model like exemplified in https://docs.djangoproject.com/en/2.1/topics/db/models/.
Basically you have to create in a project app's models.py the following class:
from django import models
class Job(models.Model):
client = models.ForeignKey(to=SomeClientModel)
appointment = models.DateTimeField()
notes = models.CharField(max_length=250)
folder = models.CharField(max_length=250)
specific_reqs = models.CharField(max_length=250)
system_notes = models.CharField(max_length=250)
I don't know what data types you actually have there, you'll have to figure that out yourself and cross-reference it to https://docs.djangoproject.com/en/2.1/ref/models/fields/#model-field-types. This was just an example for you to understand how to define it.
After you have these figured out you can do the Job.objects.create(...yourdata).
You don't need to add an id field, because Django creates one by default for all models.

notify user if similar model created _Django

I have a model.that I need to if any model with specific field created in database.send a Email to user for notify.I did some search too many apps are there for handling the notify. thats not my concern .I dont know how deploy this structure.any guide or example for this.for example :
if x = book.objects.create(title="book1") :
print("the book created")
if this action happend do something.
If you need to monitor object creation globally the best thing to use is signals
Like so:
from .models import Book
from django.db.models.signals import post_save
def book_created(sender, instance, created, **kwargs):
if created and instance.title == 'book1':
#logic here
post_save.connect(save_profile, sender=Book)
you need to stick that post_save.connect() function somewhere where it will be evaluated when the app is run, you can use app_dir/app.py for instance.

django factory boy factory with OneToOne relationship and related field

I am using Factory Boy to create test factories for my django app. The model I am having an issue with is a very basic Account model which has a OneToOne relation to the django User auth model (using django < 1.5):
# models.py
from django.contrib.auth.models import User
from django.db import models
class Account(models.Model):
user = models.OneToOneField(User)
currency = models.CharField(max_length=3, default='USD')
balance = models.CharField(max_length="5", default='0.00')
Here are my factories:
# factories.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
import factory
from models import Account
class AccountFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = Account
user = factory.SubFactory('app.factories.UserFactory')
currency = 'USD'
balance = '50.00'
class UserFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = User
username = 'bob'
account = factory.RelatedFactory(AccountFactory)
So I am expecting the factory boy to create a related UserFactory whenever AccountFactory is invoked:
# tests.py
from django.test import TestCase
from factories import AccountFactory
class AccountTest(TestCase):
def setUp(self):
self.factory = AccountFactory()
def test_factory_boy(self):
print self.factory.id
When running the test however, it looks like multiple User models are being create, and I am seeing an integriy error:
IntegrityError: column username is not unique
The documentation does mention watching out for loops when dealing with circular imports, but I am not sure whether that is whats going on, nor how I would remedy it. docs
If anyone familiar with Factory Boy could chime in or provide some insight as to what may be causing this integrity error it would be much appreciated!
I believe this is because you have a circular reference in your factory definitions. Try removing the line account = factory.RelatedFactory(AccountFactory) from the UserFactory definition. If you are always going to invoke the account creation through AccountFactory, then you shouldn't need this line.
Also, you may consider attaching a sequence to the name field, so that if you ever do need more than one account, it'll generate them automatically.
Change: username = "bob" to username = factory.Sequence(lambda n : "bob {}".format(n)) and your users will be named "bob 1", "bob 2", etc.
To pass result of calling UserFactory to AccountFactory you should use factory_related_name (docs)
Code above works next way:
AccountFactory for instantiating needs SubFactory(UserFactory).
UserFactory instantiates User.
UserFactory after instantiating calls RelatedFactory(AccountFactory)
Recursion,.. that is broken due to unique username constraint (you probably want to generate usernames via FuzzyText or Sequence)
So you need write UserFactory like this:
class UserFactory(factory.django.DjangoModelFactory):
account = factory.RelatedFactory(AccountFactory, factory_related_name='user')
username = factory.Sequence(lambda a: 'email%04d#somedomain.com' % a)
# rest of code
But you can still experience issues with already written tests. Imagine you have in tests places like next:
user = UserFactory()
account = Account(user=user)
Then adding RelatedFactory will break tests. If you haven't lots of tests and contributors in your project, you could rewrite them. But if not, it is not an option. Here is how it could be handled:
class UserFactory(factory.django.DjangoModelFactory):
class Params:
generate_account = factory.Trait(
account=factory.RelatedFactory(AccountFactory, factory_related_name='user')
)
Then code above won't be broken, because default call of UserFactory won't instantiate AccountFactory. To instantiate user with account:
user_with_account = UserFactory(generate_account=True)

Using Pre_delete Signal in django

In my app I want to keep a track of all the questions that are being deleted. And so I have created a class(table) as such in my models file.
class Deleted(models.Model):
question = models.IntegerField(null=True, blank=True)#id of question being deleted
user = models.IntegerField(null=True, blank=True)#id of user deleting the question
dt = models.DateTimeField(null=True, blank=True)#time question is deleted
When a user tries to delete a question This delete function is called:
def delete_questions(request, user, questions):
for q in questions:
q.delete()
My doubt is how can i make a pre_delete signal of django to populate the new table I have created.
~newbie trying hefty task~
Thanks in advance:)
You start off by defining the receiver you want to use:
def log_deleted_question(sender, instance, using, **kwargs):
d = Deleted()
d.question = instance.id
d.dt = datetime.datetime.now() # consider using auto_now=True in your Deleted definition
# not sure how you'd get the user via a signal,
# since it can happen from a number of places (like the command line)
d.save()
Then define your receiver decorator:
from django.db.models.signals import pre_delete
from django.dispatch import receiver
#receiver(pre_delete, sender=Question, dispatch_uid='question_delete_log')
Add it altogether:
from django.db.models.signals import pre_delete
from django.dispatch import receiver
#receiver(pre_delete, sender=Question, dispatch_uid='question_delete_signal')
def log_deleted_question(sender, instance, using, **kwargs):
d = Deleted()
d.question = instance.id
d.dt = datetime.datetime.now()
d.save()
You can put this function in your models.py file, as you know it'll be loaded and connected up correctly.
The problem though, is that you don't get the user requesting the delete. Since a delete can be triggered from the django api (command line, shell, etc), which doesn't have a request associated with it. For this reason, you might want to avoid using signals if it's absolutely critical that you store the user along with the delete.

How to modify field rendering behaviour based on state of other fields of model in django

Let's assume that I have following models:
class ScoutBook(models.Model):
troop = models.ForeignKey('Dictionary', limit_choices_to={'type' : 'Troop'}, related_name='+', blank=True, null=True)
class Dictionary(models.Model):
name = models.CharField(max_length=CHAR_FIELD_MAX_LEN, verbose_name="Nazwa")
active = models.BooleanField(verbose_name="Aktywny")
type = models.CharField(max_length=CHAR_FIELD_MAX_LEN, choices=DICTIONARY_CHOICES)
and I want to implement following logic:
when creating ScoutBook allow users to select only active troops, and when editing allow to select active troops or allow user to leave value unchanged (even if the troop is inactive). If I use limit_choices_to = {..., 'active' = True} troop that is inactive is absent from combo box in django admin.
So to be clear: let's assume that there are four troops in this system: Troop1, Troop2 and InactiveTroop, InactiveTroop2. On model creation I would like user to be able to choose Troop1 and Troop2. If model has troop field set to InactiveTroop2, I would like user to be able to choose between InactiveTroop2, Troop1 and Troop2.
I was looking at the django forms api and I didn't found obvious way do this. Moreover, in the application I'm developing there will be many such fields and many such models --- so solution must be pain free. I would rather not create new Form class for every model. I will be using mostly django admin to enable editing the database, and some read only views that will just list entries.
Ideally I would like to encapsulate this functionality in some custom field --- but fields have access to model instance on validate and save phase --- so I dont have access to it when I produce formfield.
This sounds like something you want to do in a form, not in the object itself. Create a ModelForm and override the ModelChoiceField like this:
from django import forms
class ScoutBookForm(forms.ModelForm):
troop = forms.ModelChoiceField(queryset=Troop.objects.filter(active=True))
class Meta:
model = ScoutBook
You can also override the clean method of ScoutBook to ensure it cannot ever be saved with an inactive Troop, though that may have some unintended consequences (e.g., you wouldn't be able to update a ScoutBook in the admin if the troop had gone inactive at some point in the past).
Well I had to hook into ModelForm creation. Attached Form inspects it's fields and if specific conditions are met it replaces model field queryset.
class DictionayModelForm(ModelForm):
def __init__(self, *largs, **kwargs):
super(DictionayModelForm, self).__init__(*largs, **kwargs)
if self.instance and self.instance.pk is not None:
for f in self.instance._meta.fields:
if isinstance(f, models.ForeignKey) and issubclass(f.rel.to, Dictionary):
model_field = self.fields[f.name]
value = getattr(self.instance, f.name, None)
if value and value not in model_field.choices:
model_field.queryset = Dictionary.objects.filter(Q(**f.rel.limit_choices_to) | Q(id = value.id))

Categories

Resources