Prevent m2m_changed from updating date modified of parent class - django

I have a class called Outfit and within the class, a many to many relationship called Products like this:
class Outfit(models.Model):
products = models.ManyToManyField(Product,
related_name='outfits',
blank=True)
created = models.DateTimeField(editable=False)
modified = models.DateTimeField()
class Meta:
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if not self.id:
self.created = timezone.now()
self.modified = timezone.now()
return super(Outfit, self).save(*args, **kwargs)
I have a variable called product_outfit_count in the products model which basically tracks how many outfits a particular product is used in. When an outfit is saved, this signal is called:
m2m_changed.connect(update_product_outfit_count,
sender=Outfit.products.through)
The problem is, when a product's outfit count is updated, the date modified of the outfit changes as well. How can I prevent this?
EDIT:
update_product_count method:
def update_product_outfit_count_task(product_ids):
Product = apps.get_model('products.Product')
with transaction.atomic():
for product in Product.objects.filter(pk__in=product_ids):
product.outfit_count = product.outfits.count()
product.save()

Related

Django problem updating date field when updating a record

I have a problem when I try to update a field named date_updated. My intention with the field is that every time a record is updated, the date_updated field of that record should be updated by the date the change is made. That field and one other field I have inside a Base class and then in each of the models I inherit that class to repeat the fields.
class Base(models.Model):
...
date_updated = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
self.date_updated = django.timezone.now()
super(Base, self).save(*args, **kwargs)
class Meta:
abstract = True
class OtherClass(Base):
...
My intention is that when I update any record in the OtherClass, its date_updated field will be updated.
I also tried adding the overwrite of the save method in the OtherClass, but the result is the same. The date is kept after I make the change.
I am making the change with .update(**data_to_update)
I did this when i wanted to update only the updated_on (datetime) column:
This might help you:
from datetime import datetime
def get_current_datetime_str():
now = datetime.now()
return now.strftime("%Y-%m-%d %H:%M:%S")
class ModelName(models.Model):
date_updated=models.CharField(max_length=100) #whatever your field is
...
def __str__(self):
return self.name
continue and write this below def str(self):
def save(self, *args, **kwargs):
self.date_updated = get_current_datetime_str()
super().save(*args, **kwargs)

How to create or update value to relationship model

I want to save the Portfolio products details in PortfolioProducts model in django
I have models like below:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
Portfolio form:
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
fields = ['name']
My view file:
def edit(request):
portfolio_form = PortfolioForm
if request.method=="POST":
portfolio_id=request.POST.get('portfolio_id')
portfolio_detail = Portfolio.objects.get(pk=portfolio_id)
pform = portfolio_form(request.POST, instance=portfolio_detail)
if pform.is_valid():
portfolio = pform.save(commit = False)
portfolio.save()
products=request.POST.getlist('product_id[]')
for product in products:
ppform = PortfolioProducts(product_id=product, portfolio_id=portfolio_id)
port_product = ppform.save()
I am trying to save and update the Portfolio products like this, but is adding products to portfolio multiple time.
Well, you don't need to update PortfolioProduct for updating Portofilio. Because even if you update Portfolio, its primary key remains same as before. So the relationship remains the same.
But, in your case, if PortofolioProduct does not exist for a product in products and Portfolio object, then you can create one like this:
for product in products:
ppform, _ = PortfolioProducts.objects.get_or_create(product_id=product, portfolio_id=portfolio_id)
Update
From comments: you need to either remove def save(self): methods from you Model(Because you are not doing anything particular in those save methods) or if intend to keep you save() methods, then you need to call the super properly, like this:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Portfolio, self).save(*args, **kwargs)
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
def save(self, *args, **kwargs):
super(PortfolioProducts, self).save(*args, **kwargs)
Yes, I also got stuck with the same issue in my django project. The thing it does in my case was everytime the user tries to update his/her profile, it created a new one, this is because of the Foreign Key to it. I fixed the issue by deleting the previous user profile (in your case it's portfolio) every time the user updates it.
class UserEdit(TemplateView):
template_name = 'accounts/homee.html'
def get(self, request):
form = UserProfilee()
ppp = UserProfile.objects.get(user=request.user)
return render(request, self.template_name, {'form': form, 'ppp': ppp})
def post(self, request):
form = UserProfilee(request.POST, request.FILES)
pppp = UserProfile.objects.get(user=request.user)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
if not post.image:
post.image = pppp.image
UserProfile.objects.filter(user=post.user).delete()
post.save()
return redirect('/home/homepage/')
args = {'form': form}
return render(request, self.template_name, args)
As you see,I filter the user and delete the user profile whenever user updates his/her profile thus leaving only 1 user profile.

Django Use Many To Many with Through in Form

I have a data model where I am using a manual intermediate table for a m2m relationship.
Building on the classical example from the django doc:
from django.db import models
INSTRUMENT_CHOICES = (
('guitar', 'Guitar'),
('bass', 'Bass Guitar'),
('drum', 'Drum'),
('keyboard', 'Keyboard'),
)
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Leadership')
def __str__(self):
return self.name
def get_leadership():
return self.leadership_set.first()
class Leadership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
instrument = models.CharField('Playing Instrument', choices=INSTRUMENT_CHOICES,
max_length=15,
null=True,
blank=False)
class Meta:
unique_together = ('person', 'group')
When I create a new group I also want to specify who is going to be the leader, and for this relationship also specify which instrument he will play in that group.
What really confuses me, given also the lack of documentation on this topic is how to handle this kind of relationship in forms.
This is the form I came with:
class InstrumentField(forms.ChoiceField):
def __init__(self, *args, **kwargs):
super().__init__(INSTRUMENT_CHOICES, *args, **kwargs)
class GroupForm(forms.ModelForm):
instrument = InstrumentField(required=False)
class Meta:
model = Group
fields = ['name',
'members'
'instrument'] # This works but it's not correctly initalized in case of edit form
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk is not None: # editing
# PROBLEM: this doesn't work
self.fields["instrument"].initial = self.instance.get_leadership().instrument
def save(self, commit=True):
group = super().save(commit=False)
if commit:
group.save()
if 'instrument' in self.changed_data:
leader = self.cleaned_data.get('members').first()
instrument = self.cleaned_data['instrument']
Leadership.objects.update_or_create(person=leader, group=group, defaults={'instrument': instrument})
return group
As suggested in the django doc I am manually instantiating Leadership objects (see the form save method).
What I couldn't solve is how to populate the instrument field in case of form editing. I try to do this in the __init__: first I check that we are in "edit" mode (the instance has a pk) then I get the relevant Leadership object (see Group.get_leadership) and from that I extract the instrument and I assign it to the fields["instrument"].initial.
This doesn't work.
I could inspect that the initial value was set but then when I render the form the default choice value is shown (the first value of the INSTRUMENT_CHOICES).
What am I missing here?
Is there a better way or a better docs on how to handle m2m with through model in forms?

get total price for the products in m2m field in save

Django 1.8 I'm trying to get total price for the products in m2m field.
from django.models.db import Sum
class Product(models.Model):
...
price = models.DecimalField(default=0, max_digits=9, decimal_places=2)
class Order(models.Model):
...
products = models.ManyToManyField(Product, related_name='orders')
total = models.DecimalField()
...
def save(self, *args, **kwargs):
self.total = self.products.all().annotate(Sum('price'))
super(Order, self).save(*args, **kwargs)
When I try to save the Order object, the code above produce ValueError:
"<Order: None>" needs to have a value for field "order" before this
many-to-many relationship can be used.
Do you have specific requirements to save this total in your database? If not,
class Order(models.Model):
...
products = models.ManyToManyField(Product, related_name='orders')
...
#property
def total(self):
result = self.products.aggregate(Sum('price'))
return result['price__sum']
the best way to do it, it's by a signal:
https://docs.djangoproject.com/en/1.8/ref/signals/#m2m-changed
here it's a example:
#receiver(m2m_changed, sender=BusquedaInmueble.zonas.through)
def post_save_busqueda_m2m (sender, action, instance, *args, **kwargs):
busqueda = instance
if action == 'post_add':

'Business' instance needs to have a primary key value before a many-to-many relationship can be used

I've changed the django-registration code. I am inserting data in UserProfile and Business model during signup.
Data is saving in UserProfile Model.
#TODO: saving contact and address field data into UserProfile
user_profile = new_user.get_profile()
user_profile.user = new_user
user_profile.contact, user_profile.address = contact, kwargs['address']
user_profile.save()
Following code does not work.Getting this error.
'Business' instance needs to have a primary key value before a many-to-many relationship can be used.
#TODO: saving business field data into Business Model
user_business = Business()
user_business.owner = new_user
user_business.name = business
user_business.save()
thanks
UPDATE
class Business(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
owner = models.ManyToManyField(User)
created = models.DateTimeField(editable=False, default=datetime.now)
modified = models.DateTimeField(editable=False, default=datetime.now)
class Meta:
ordering = ['name']
def save(self):
self.modified = datetime.now()
if not self.slug:
self.slug = slugify(self.name, instance=self)
super(Business, self).save()
Try updating your custom code to :
def save(self, *args, **kwargs):
self.modified = datetime.now()
if not self.slug:
self.slug = slugify(self.name)
super(Business, self).save(*args, **kwargs)
UPDATE
#no_access I think there is a problem in the process of assigning the User instance to the ManyToManyField in Business. I suspect that the ManyToManyField field isn't getting the reference to the User instance that is being created. The intermediate table of ManyToManyField field needs a proper User object to reference to. So, I think here lies the problem.