I'm a newbie in Django, so can you help me to understand how the save() method works?
Here's my models:
class Tag(models.Model):
name = models.CharField(verbose_name=u'Tag', max_length=200, unique=True)
class Entry(models.Model):
title = models.CharField(verbose_name=u'Entry title', max_length=200)
# some more fields here
tags_string = models.CharField(verbose_name=u'Tags', max_length=200, blank=True)
tags = models.ManyToManyField(Tag, blank=True)
There is tags_string where user enters tags separated by comma. It's just a string.
Then I'm trying to add tags to ManyToManyField by clicking "Save" in Django admin:
def save(self):
super(Entry, self).save()
if self.tags_string:
for tag in tags_string.split(","):
t = Tag.objects.create(name=tag)
self.tags.add(t)
but it doesn't work. entry.tags.add(t) works perfectly through the Django shell - it adds the values to the database. I think that something is wrong in my save() method.
Could you suggest me how to fix it, please?
try this
def save(self):
super(Entry, self).save()
if self.tags_string:
for tag in tags_string.split(","):
self.tags.create(name=tag)
Check the M2M tags format and print those?
def save(self):
super(Entry, self).save()
if self.tags_string:
print self.tags,type(self.tags)
for tag in tags_string.split(","):
.......
First, save has additional parameters that you need to account for. Second, you should be using get_or_create instead of create for the tags:
def save(self, *args, **kwargs):
super(Entry, self).save(*args, **kwargs)
if self.tags_string:
for tag in tags_string.split(","):
t, created = Tag.objects.get_or_create(name=tag)
self.tags.add(t)
Those may not fix the current issue, but it would have got you eventually.
You should also probably be doing some sort of normalization on the tags as well, using str.lower() or title() from django.template.defaultfilters. Otherwise, you'll end up with "Tag", "tag", "TAG" and "tAg".
Related
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()
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.
I want to iterate over my model fields within a Django model and check if they are empty string and replace them with a null in the model save() method programmatically. This is because some CharFields need to be unique or have no value.
Example:
class Person(models.Model):
name = models.CharField(blank=True, unique=True, null=True)
nick_name = models.CharField(blank=True, unique=True, null=True)
...
age = models.IntegerField()
def save(self, *args, **kwargs):
for field in self._meta.fields: # get the model fields
if field=='':
field = None
super(Person, self).save(*args, **kwargs)
The above complains about a creation_counter which is unclear why an attempt to compare those values instead of the field value with the empty string is done.
This could be done manually, but I have too many models....
Any suggestions?
edit:
Thanks to everyone who attempted to help me! :D
The solution that seems to work for me is posted by Jazz, but his code isn't showing up in his post. This is my version which is essentially identical, but with an extra check to make sure we are only overriding when necessary:
from django.db.models.Field import CharField as _CharField
class CharField(_CharField):
def get_db_prep_value(self, value, *args, **kwargs):
if self.blank == self.null == self.unique == True and value == '':
value = None
return super(CharField).get_db_prep_value(value, *args, **kwargs)
In your case, I would suggest a custom model field, which subclasses a CharField and ensures that a empty string is converted to None -- overriding get_db_prep_value should do it.
class EmptyStringToNoneField(models.CharField):
def get_prep_value(self, value):
if value == '':
return None
return value
class Person(models.Model):
name = EmptyStringToNoneField(blank=True, unique=True, null=True)
nick_name = EmptyStringToNoneField(blank=True, unique=True, null=True)
https://docs.djangoproject.com/en/dev/howto/custom-model-fields/
Jazz's answer works like a charm for me.
I just want to point out that if you are using South migration tool, the schemamigration command will fail, unless you specify a introspect rule for the new custom field.
For "simple fields", you can simply add the following code to wherever your field is specified.
from south.modelsinspector import add_introspection_rules
add_introspection_rules([], ["^myapp\.stuff\.fields\.SomeNewField"])
Now the South migration should work.
FYI.
Reference: http://south.aeracode.org/wiki/MyFieldsDontWork
I need to add a colorpicker to my django model and wrote a custom widget. However when I add this colordfield to my model, django gives this error:
column mediaplanner_ievent.color does not exist
LINE 1: ...nt"."bits", "mediaplanner_ievent"."capture_link", "mediaplan...
My model is :
from mediaplanner.custom_widgets import ColorPickerWidget
class ColorField(models.CharField):
def __init__(self,*args, **kwargs):
kwargs['max_length'] = 10
super(ColorField, self).__init__(*args, **kwargs)
def formfield(self, **kwargs):
kwargs['widget'] = ColorPickerWidget
return super(ColorField, self).formfield(**kwargs)
class iEvent(models.Model):
name = models.CharField(verbose_name= u"Uygulama Adı", max_length=100, unique=True)
bits = models.CommaSeparatedIntegerField(verbose_name= u"Bitler",max_length=100)
capture_link = models.URLField(verbose_name= u"Capture URL", null=True, blank=True)
color = ColorField(blank=true)
class Meta:
verbose_name = u"red button"
verbose_name_plural = u"red buttonlar"
def __unicode__(self):
return smart_str( "%s"% self.name )
The strange thing is, when I looked my database, there exist colorfield. I don't want to delete the db and load it again. But ofcourse if it's the only solution, then no choice ..
So someone can help me how to solve it?
The field in your database is named colorfield bu the field in your model is named color. You have to change one or the other to make it work again.
Can anyone tell me how i can limit the choices for the Page model which i inherit from in the following code?
class CaseStudy(Page):
"""
An entry in a fancy picture flow widget for a case study page
"""
image = models.ForeignKey(Image, limit_choices_to={'is_active': True, 'category__code':'RP'})
def __unicode__(self):
return u"%s" % self.title
The django admin is limiting the image choices in a drop down successfully, but i would like to limit a field in the Page model as well (a 'parent page field'), ie:
class Page(models.Model):
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
I managed to work this out - by overriding the admin model form. I realise this could be tightened up, but thought it might come in use to someone out there. Here's an excerpt from the admin.py
class CaseStudyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CaseStudyForm, self).__init__(*args, **kwargs)
recent_project_page = Page.objects.get(title="Recent Projects")
parent_widget = self.fields['parent'].widget
choices = []
for key, value in parent_widget.choices:
if key in [recent_project_page.id,]:
choices.append((key, value))
parent_widget.choices = choices
class CaseStudyAdmin(admin.ModelAdmin):
form = CaseStudyForm
admin.site.register(CaseStudy, CaseStudyAdmin)