How to update model field after saving instance? - django

I have a Django model Article and after saving an instance, I want to find the five most common words (seedwords) of that article and save the article in a different model Group, based on the seedwords.
The problem is that I want the Group to be dependent on the instances of Article, i.e. every time an Article is saved, I want Django to automatically check all existing groups and if there is no good fit, create a new Group.
class Article(models.Model):
seedword_group = models.ForeignKey("Group", null=True, blank=True)
def update_seedword_group(self):
objects = News.objects.all()
seedword_group = *some_other_function*
return seedword_group
def save(self, *args, **kwargs):
self.update_seedword_group()
super().save(*args, **kwargs)
class Group(models.Model):
*some_fields*
I have tried with signals but couldn't work it out and I'm starting to think I might have misunderstood something basic.
So, to paraphrase:
I want to save an instance of a model A.
Upon saving, I want to create or update an existing model B depending on A via ForeignKey.

Honestly I couldn't understand the rationale behind your need but I guess below code may help:
def update_seedword_group(content):
objects = News.objects.all()
"""
process word count algorithm and get related group
or create a new group
"""
if found:
seedword_group = "*some_other_function*"
else:
seedword_group = Group(name="newGroup")
seedword_group.save()
return seedword_group
class Group(models.Model):
*some_fields*
class Article(models.Model):
seedword_group = models.ForeignKey("Group", null=True, blank=True)
content = models.TextField()
def save(self):
self.group = update_seedword_group(self.content)
super().save()

Related

How to limit add to a model in django

How to limit add to a model in django ? i would like to create only one company on this model, only one, so if user want to add more, it s not possible.
class Company(models.Model):
name = models.CharField(max_length=64)
address = models.TextField(max_length=250)
One of solutions is to override save method on Company
class Company(models.Model):
name = models.CharField(max_length=64)
address = models.TextField(max_length=250)
def save(self, *args, **kwargs):
if Company.objects.exists():
return
super().save(*args, **kwargs)
but your question is rather short and not precise so that's probably not the way you want it to behave

either or fields django models

Hi i am creating a django model for a type of subscription for books
class Subscription(models.Model):
user = models.ForeignKey(User,null=True, blank=True)
group = models.ForeignKey(Group, null=True, blank=True)
book_list = models.ForeignKey(Books, null=True)
def create(cls, (user, group), **kwds):
return cls(user=user, group=group, books=books, **kwds)
I want to create this in such a way that either the field user is chosen or the field Group but not both
the above syntax gives an error " User object not iterable"
can anyone help me with this?
Thanks in advance
First thing if you wish to create as such, one suggestion would be add a Manager to this model, for eg. SubscriptionManager and in that write create method. It could be something like this.
def create(self, subscriber, **kwds):
if isinstance(subscriber, User):
#save subscriber instance in user field
elif isinstance(subsriber, Group):
#save subscriber instance is group field
PS: I have only provided solution for either or question, take care of books_list as well

M2M using through and form with multiple checkboxes

I'd like to create a form allowing me to assign services to supplier from these models. There is no M2M relationship defined since I use a DB used by others program, so it seems not possible to change it. I might be wrong with that too.
class Service(models.Model):
name = models.CharField(max_length=30L, blank=True)
class ServiceUser(models.Model):
service = models.ForeignKey(Service, null=False, blank=False)
contact = models.ForeignKey(Contact, null=False, blank=False)
class SupplierPrice(models.Model):
service_user = models.ForeignKey('ServiceUser')
price_type = models.IntegerField(choices=PRICE_TYPES)
price = models.DecimalField(max_digits=10, decimal_places=4)
I've created this form:
class SupplierServiceForm(ModelForm):
class Meta:
services = ModelMultipleChoiceField(queryset=Service.objects.all())
model = ServiceUser
widgets = {
'service': CheckboxSelectMultiple(),
'contact': HiddenInput(),
}
Here is the view I started to work on without any success:
class SupplierServiceUpdateView(FormActionMixin, TemplateView):
def get_context_data(self, **kwargs):
supplier = Contact.objects.get(pk=self.kwargs.get('pk'))
service_user = ServiceUser.objects.filter(contact=supplier)
form = SupplierServiceForm(instance=service_user)
return {'form': form}
I have the feeling that something is wrong in the way I'm trying to do it. I have a correct form displayed but it is not instantiated with the contact and checkboxes aren't checked even if a supplier has already some entries in service_user.
You are defining services inside your Meta class. Put it outside, right after the beginning of SupplierServiceForm. At the very least it should show up then.
Edit:
I misunderstood your objective. It seems you want to show a multiple select for a field that can only have 1 value. Your service field will not be able to store the multiple services.
So, by definition, your ServiceUser can have only one Service.
If you don't want to modify the database because of other apps using it, you can create another field with a many to many relationship to Service. That could cause conflicts with other parts of your apps using the old field, but without modifying the relationship i don't see another way.
The solution to my problem was indeed to redefine my models in oder to integrate the m2m relationship that was missing, using the through argument. Then I had to adapt a form with a special init method to have all selected services displayed in checkboxes, and a special save() method to save the form using m2m relationship.
class Supplier(Contact):
services = models.ManyToManyField('Service', through='SupplierPrice')
class Service(models.Model):
name = models.CharField(max_length=30L, blank=True)
class ServiceUser(models.Model):
service = models.ForeignKey(Service, null=False, blank=False)
supplier = models.ForeignKey(Supplier, null=False, blank=False)
price = models.Decimal(max_digits=10, decimal_places=2, default=0)
And the form, adapted from the very famous post about toppings and pizza stuff.
class SupplierServiceForm(ModelForm):
class Meta:
model = Supplier
fields = ('services',)
widgets = {
'services': CheckboxSelectMultiple(),
'contact_ptr_id': HiddenInput(),
}
services = ModelMultipleChoiceField(queryset=Service.objects.all(), required=False)
def __init__(self, *args, **kwargs):
# Here kwargs should contain an instance of Supplier
if 'instance' in kwargs:
# We get the 'initial' keyword argument or initialize it
# as a dict if it didn't exist.
initial = kwargs.setdefault('initial', {})
# The widget for a ModelMultipleChoiceField expects
# a list of primary key for the selected data (checked boxes).
initial['services'] = [s.pk for s in kwargs['instance'].services.all()]
ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
supplier = ModelForm.save(self, False)
# Prepare a 'save_m2m' method for the form,
def save_m2m():
new_services = self.cleaned_data['services']
old_services = supplier.services.all()
for service in old_services:
if service not in new_services:
service.delete()
for service in new_services:
if service not in old_services:
SupplierPrice.objects.create(supplier=supplier, service=service)
self.save_m2m = save_m2m
# Do we need to save all changes now?
if commit:
self.save_m2m()
return supplier
This changed my first models and will make a mess in my old DB but at least it works.

Django form save or model save with a select field?

So I'm still new to Django. I have a single field in my form. And I was just wondering whether or not I need a form save function or a model save function? When is it appropriate to use either or?
For instance: My form:
class OpinionStatusForm(forms.Form):
choices = (('0', "Your Status"), ('1', "This"), ('2', "That"), ('3', "The Other"))
status = forms.CharField(max_length=2, widget=forms.Select(choices=choices, attrs={'class':'status_dropdown','onchange': 'this.form.submit();'}), required=False)
def save(self, opinion_status):
opinion_status.status = self.cleaned_data['status']
My model:
class OptionStatus(models.Model):
user = models.ForeignKey(User, null=True, unique=True)
status = models.CharField(max_length=2, choices=opinion_statuses)
opinion = models.ForeignKey(Opinion, null=True, blank=True)
def __unicode__(self):
return self.status
def save(self, *args, **kwargs):
super(OpinionStatus, self).save(*args, **kwargs)
I'm going to be ajax-ing the form. I don't know if that makes a difference or not. Thanks!
What you actually need, is a ModelForm. In your example you're working with a standard forms.Form. This is not bound to a model instance. As a result, there's also no need for a save method. The best examples are really given inside the Django docs:
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
Go step by step over the code examples and you'll understand. It would be too much to explain it all in one Stackoverflow answer - and the Django docs are incredible thorough.

Django Unique Slug by id

class Product(models.Model):
title = models.CharField(max_length=75)
class Deal(models.Model):
product = models.ForeignKey(Product)
slug = models.SlugField(max_length=255, unique=True)
Having a similar basic setup as above, I want to generate unique slugs for each Deal instance using product title of it's deal and id of the deal itself. IE: "apple-iphone-4s-161" where 161 is the id of the deal and the text before is the title of the product.
For this, how can I overwrite the save() method of the Deal model to apply it?
Of course you can simply overwrite save() method on model (or make receiver for post_save signal).
It will be something like:
from django.template.defaultfilters import slugify
class Deal(models.Model):
product = models.ForeignKey(Product)
slug = models.SlugField(max_length=255, unique=True)
def save(self, *args, **kwargs):
super(Deal, self).save(*args, **kwargs)
if not self.slug:
self.slug = slugify(self.product.title) + "-" + str(self.id)
self.save()
But what is ugly in this solution is that it will hit database twice (it is saved two times). It is because when creating new Deal object it will not have id until you save it for the first time (and you cannot do much about it).
i've bumped at this problem and tested the jasisz solution, and got the max recursion depth exceeded error, so i've fiddle it little and this is how looks for me:
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Node, self).save(*args, **kwargs)
You could edit this to suit your needs, it tests if this records exists, if not then it creates the slug field otherwise is update and no need for modifieng the slug field.
hope it helps.
You should not have your id in that slug field at all. The two main reasons for this are:
It creates the problem you are having
It's redundant data in the database – the id is already stored
But there is a simple solution to your problem: The only reason why the slug is stored in the database is so you can find a row by slug. But you don't need that – you have the ID. So you should do this:
class DealManager(models.Manager):
def get_by_slug(slug):
return self.get(id=slug.rsplit('-', 1)[1])
class Deal(models.Model):
product = models.ForeignKey(Product)
objects = DealManager()
#property
def slug(self):
return slugify(f'{self.name}-f{self.id}')
In your view or wherever you need to retrieve an item given a slug, you can just do Deal.objects.get_by_slug(slug).
I know this may not exactly work for your situation, but I remember bumping into a similar case. I think I had a created_at field in my model that has auto_now=True. Or something simillar
My slug would look like like this
self.slug = '%s-%s' %(
slugify(self.title),
self.created_at
)
or you can have
self.slug = '%s-%s' %(
slugify(self.title),
datetime.datetime.now()
)
just make sure that the slug max_length is long enough to include the full created_at time, as well as the title, so that you don't end up with non-unique or over max length exceptions.