How long should this take to run?
query = Contact.objects.filter(contact_owner=batch.user, subscribed=True)
objs = [
Message(
recipient_number=e.mobile,
content=content,
sender=e.contact_owner,
billee=user,
sender_name=sender,
)
for e in query
Just this code nothing else (not saving to db I'm only running above i.e. create the objects). It takes 15 mins for 5000 messages objects to be created in the query. Is this right? Why is Django so slow?
This is the model it creates, again I'm not saving here. I think there ahs to be an issue in the model when an object is created, that or Django is just too slow for my needs.
Model message
from django.db import models
from django.contrib.contenttypes import generic
from django.utils.translation import ugettext as _
from django.conf import settings
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
import uuidfield.fields
import picklefield
import jsonfield
if 'timezones' in settings.INSTALLED_APPS:
from timezones.utils import adjust_datetime_to_timezone
else:
def adjust_datetime_to_timezone(a, b, c):
return a
from gateway import Gateway
class MessageManager(models.Manager):
def get_matching_message(self, datadict):
for gateway in Gateway.objects.all():
try:
return Message.objects.get(
gateway_message_id=datadict.get(gateway.status_msg_id),
gateway=gateway,
)
except Message.DoesNotExist:
pass
def get_original_for_reply(self, datadict):
for gateway in Gateway.objects.all():
try:
return Message.objects.get(
uuid=datadict.get(gateway.uuid_keyword),
gateway=gateway
)
except Message.DoesNotExist:
pass
# This may have been a message sent from another phone, but
# there may be a reply-code that was added in.
return self.custom_reply_matcher(datadict)
def custom_reply_matcher(self, datadict):
# Designed to be overridden.
return None
def get_last_rate_for(self, recipient_number):
m = Message.objects.filter(recipient_number=recipient_number).exclude(
gateway_charge=None).order_by('-send_date')[0]
return m.gateway_charge / m.length
def get_message(self, gateway_message_id):
try:
return Message.objects.get(gateway_message_id=gateway_message_id,)
except Message.DoesNotExist:
pass
MESSAGE_STATUSES = (
('Unsent', 'Unsent'),
('Sent', 'Sent'),
('Delivered', 'Delivered'),
('Failed', 'Failed'),
)
class Message(models.Model):
"""
A Message.
We have a uuid, which is our reference. We also have a gateway_message_id,
which is their reference. This is required by some systems so we can
pass in a unique value that will allow us to match up replies to original
messages.
"""
content = models.TextField(help_text=_(u'The body of the message.'))
recipient_number = models.CharField(max_length=32,
help_text=_(u'The international number of the recipient'
', without the leading +'))
sender = models.ForeignKey('auth.User', related_name='sent_sms_messages')
sender_name = models.CharField(max_length=11)
send_date = models.DateTimeField(null=True, blank=True, editable=False)
delivery_date = models.DateTimeField(null=True, blank=True, editable=False,
help_text="The date the message was sent.")
uuid = uuidfield.fields.UUIDField(auto=True,
help_text=_(u'Used for associating replies.'))
status = models.CharField(max_length=16, choices=MESSAGE_STATUSES,
default="Unsent",
)
status_message = models.CharField(max_length=128, null=True, blank=True)
billed = models.BooleanField(default=False)
content_type = models.ForeignKey('contenttypes.ContentType')
object_id = models.PositiveIntegerField()
billee = generic.GenericForeignKey()
gateway = models.ForeignKey('sms.Gateway',
null=True, blank=True, editable=False)
gateway_message_id = models.CharField(max_length=128,
blank=True, null=True, editable=False)
reply_callback = picklefield.PickledObjectField(null=True, blank=True)
gateway_charge = models.DecimalField(max_digits=10, decimal_places=5,
null=True, blank=True)
charge = models.DecimalField(max_digits=10, decimal_places=5,
null=True, blank=True)
objects = MessageManager()
class Meta:
app_label = 'sms'
permissions = (
('view_message', 'Can view message'),
)
ordering = ('send_date',)
def send(self, gateway):
gateway.send(self)
#property
def length(self):
"""Unicode messages are limited to 70 chars/message segment."""
# try:
# return len(str(self.content)) / 160 + 1
# except UnicodeEncodeError:
# return len(self.content) / 70 + 1
return len(self.content) / 160 + 1
#property
def local_send_time(self):
# TODO: Get this from UserProfile?
if getattr(self.billee, 'timezone', None):
return adjust_datetime_to_timezone(
self.send_date,
settings.TIME_ZONE,
self.billee.timezone
)
return self.send_date
#property
def local_send_date(self):
return self.local_send_time.date()
def __unicode__(self):
return "[%s] Sent to %s by %s at %s [%i]" % (
self.status,
self.recipient_number,
self.sender,
self.send_date,
self.length
)
#receiver(pre_save, sender=Message)
def my_handler(sender, **kwargs):
instance = kwargs['instance']
if not instance.charge:
instance.charge = instance.length
# No need to save, as we're slipping the value in
# before we hit the database.
contact model
import os
import datetime
from uuid import uuid4
from datetime import date
from django.db import models
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from django.utils import timezone
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver
from adaptor.fields import *
from adaptor.model import CsvModel
def path_and_rename(path):
"""
Callable function for renaming the file being uploaded.
"""
def wrapper(instance, filename):
ext = filename.split('.')[-1]
# get filename
if instance.pk:
filename = '{}.{}'.format(instance.pk, ext)
else:
# set filename as random string
filename = '{}.{}'.format(uuid4().hex, ext)
# return the whole path to the file
return os.path.join(path, filename)
return wrapper
class GroupManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(user=user, )
class Group(models.Model):
"""
Stores all groups.
"""
name = models.CharField(max_length=60)
modified = models.DateTimeField(null=True, auto_now=True, help_text="Shows when object was modified.")
created = models.DateTimeField(auto_now_add=True, help_text="Shows when object was created.")
#FK
user = models.ForeignKey(User, related_name="user")
objects = GroupManager()
def __unicode__(self):
return self.name
def get_absolute_url(self):
return reverse('contacts.views.group', args=[str(self.id)])
def get_delete_url(self):
return reverse('contacts.views.group_delete_confirm', args=[str(self.id)])
class ContactManager(models.Manager):
"""
Custom Manager for keyword.
"""
def unsorted_contacts(self, user):
"""
Manager that will list all records for a user where group is 'None'.
"""
return self.get_query_set().filter(contact_owner=user, group=None)
def for_user_and_group(self, user, group):
"""
Manager that will list all records for a user where group is 'group'.
"""
return self.get_query_set().filter(contact_owner=user, group=group)
def for_user(self, user):
"""
Manager that will list all records for a user they own.
"""
return self.get_query_set().filter(contact_owner=user)
class Contact(models.Model):
"""
Stores all contacts.
"""
first_name = models.CharField(max_length=60, blank=True)
last_name = models.CharField(max_length=60, blank=True)
company = models.CharField(max_length=100, blank=True)
mobile = models.CharField(max_length=15)
email = models.EmailField(max_length=100, blank=True)
subscribed = models.NullBooleanField(default=1, help_text="Shows if contact is unsubscribed to SMS/Email.")
modified = models.DateTimeField(null=True, auto_now=True, help_text="Shows when object was modified.")
created = models.DateTimeField(auto_now_add=True, help_text="Shows when object was created.")
objects = ContactManager()
#FK
group = models.ForeignKey(Group, related_name='contacts', blank=True, null=True)
contact_owner = models.ForeignKey(User)
def __unicode__(self):
return self.first_name
def full_name(self):
return "%s %s" % (self.first_name, self.last_name)
def get_delete_url(self):
return reverse('contacts.views.contact_delete', args=[str(self.id), str(self.group_id)])
def get_group_absolute_url(self):
return reverse('contacts.views.group', args=[str(self.group_id)])
#property
def user(self):
return self.contact_owner
#receiver(pre_delete, sender=Contact)
def contact_cleanup(sender, instance, **kwargs):
"""
Do a bit of tidying up when deleting a Contact.
Sent at the beginning of a model's delete() method and a queryset's delete() method.
"""
# Remove any FK's not done by cascade delete like generic relationships.
from unsubscribe.models import Unsubscribe
unsubscribe_list = Unsubscribe.objects.filter(object_id=instance.id, content_type__model='contact')
unsubscribe_list.delete()
class Upload(models.Model):
"""
Stores jobs and status uploads of file uploads for CSV import.
"""
filepath = models.FileField(upload_to=path_and_rename('uploadsCSV'),
help_text="It can take several minutes for contacts to appear.")
# Upload audit information
uploaded_by = models.ForeignKey(User)
date_uploaded = models.DateTimeField(auto_now_add=True)
# Processing audit information
PENDING, PROCESSED, FAILED = 'Pending', 'Processed', 'Failed'
STATUSES = (
(PENDING, _(PENDING)),
(PROCESSED, _(PROCESSED)),
(FAILED, _(FAILED)),
)
status = models.CharField(max_length=64, choices=STATUSES, default=PENDING)
processing_description = models.TextField(blank=True, null=True)
num_records = models.PositiveIntegerField()
num_columns = models.PositiveIntegerField()
date_start_processing = models.DateTimeField(null=True)
date_end_processing = models.DateTimeField(null=True)
#FKs
group = models.ForeignKey(Group)
def get_configurator_url(self):
return reverse('contacts.views.upload_configurator', args=[str(self.id)])
def process(self, cleaned_data):
self.date_start_processing = timezone.now()
try:
group_position = self.num_columns + 1
upload_id_position = self.num_columns + 1
# Try and import CSV
import_this(data=self.filepath, extra_fields=[
{'value': self.group_id, 'position': group_position},
{'value': self.uploaded_by.id, 'position': upload_id_position}], cleaned_data=cleaned_data)
self._mark_processed(self.num_records)
except Exception as e:
self._mark_failed(unicode(e))
def was_processing_successful(self):
return self.status == self.PROCESSED
def was_processing_successful(self):
return self.status == self.PROCESSED
def _mark_processed(self, num_records, description=None):
self.status = self.PROCESSED
self.date_end_processing = date.today()
self.num_records = num_records
self.processing_description = description
self.save()
def _mark_failed(self, description):
self.status = self.FAILED
self.processing_description = description
self.save()
def import_this(cleaned_data, *args, **kw):
# make custom ContactCSVModel
class ContactCSVModel(CsvModel):
for k, v in cleaned_data.items():
if not v == '':
# print("---------------------------------")
# print(str(v))
# print("---")
# print(k[3:])
# print("---------------------------------")
setattr(CsvModel, v, CharField(row_num=k[3:]))
group = DjangoModelField(Group, row_num="5")
contact_owner = DjangoModelField(User, row_num="6")
class Meta:
delimiter = ","
dbModel = Contact
update = {'keys': ["mobile", "group"]}
return ContactCSVModel.import_data(*args, **kw)
If you install Django debugtoolbar you could actually see the queries being fired, and the code responsible on firing them.
This slowness is mainly because django fires a new db query every time you do e.mobile and e.contact_owner, in the loop.
To prevent these queries, prefetch the data using select_related like below
query = Contact.objects.select_related('mobile', 'contact_owner').filter(contact_owner=batch.user, subscribed=True)
If your relations are many to many, then use prefetch_related rather than select_related.
Related
I will create a different author table using the user table available in django and I want to combine this custom table I created with the column in the post model. Thus, I will have both the author table and the post table with the posts shared by these authors separately. And also, I will make the user models that I will create in the future with the ready user table. but I am getting the error I mentioned.
When i add authors to admin panel I get this error. When I add 1 author, there is no problem, but when I add 2nd author, I get this error. How can I solve this, what are the alternative ways?
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.urls import reverse
from .utils import get_read_time
from django.conf import settings
from django.utils import timezone
from markdown_deux import markdown
from django.utils.text import slugify
from django.contrib.auth.models import User
from django.db.models.signals import pre_save
from django.utils.safestring import mark_safe
class GENDER(models.TextChoices):
Erkek = "Erkek"
Kadın = "Kadın"
Diğer = "Diğer"
NONE = "Belirtmek İstemiyorum"
class Category_Choices(models.TextChoices):
BİLİNMEYENLER = '13İlinmeyenler'
KİŞİSEL_GELİŞİM = 'Kişisel Gelişim'
İLHAM_AL = 'İlham Al'
YATIRIM_HABERLERİ = 'Yatırım Haberleri'
GİRİŞİMCİLİK = 'Girişimcilik'
ENGLİSH_NEWS = 'English News'
BAŞARI_HİKAYELERİ = "Başarı Hikayeleri"
class Color_Choices(models.TextChoices):
INDIGO = 'indigo'
green = 'green'
yellow = 'yellow'
RED = 'red'
blue = 'blue'
gray = 'gray'
pink = "pink"
class Author(models.Model):
author = models.ForeignKey(User, on_delete = models.CASCADE)
firstName = models.CharField(max_length=80)
lastName = models.CharField(max_length=80)
email = models.EmailField(null=True,blank=True)
displayName = models.CharField(max_length=100)
gender = models.TextField(blank=False,choices=GENDER.choices)
avatar = models.ImageField(upload_to="avatar")
href = models.SlugField(unique=True,editable=False)
desc = models.TextField()
jobName = models.CharField(default="Yazar",max_length=30)
bgImage = models.ImageField(null=True, blank=True, upload_to="background")
def __str__(self):
return self.displayName
def get_slug(self):
href = slugify(self.displayName.replace("ı","i"))
unique = href
number = 1
while Author.objects.filter(slug=unique).exists():
unique = "{}-{}" .format(href,number)
number += 1
return unique
class Categories(models.Model):
name = models.CharField(max_length=50,choices=Category_Choices.choices,default=Category_Choices.BİLİNMEYENLER)
color = models.CharField(max_length=30,choices=Color_Choices.choices, default=Color_Choices.RED)
href = models.SlugField(unique=True,editable=False)
thumbnail = models.ImageField(upload_to="postpost",null=True,blank=True)
def __str__(self):
return self.name
def get_category_slug(self):
slug = slugify(self.name.replace("ı","i"))
unique = slug
number = 1
while Categories.objects.filter(slug=unique).exists():
unique = "{}-{}" .format(slug,number)
number += 1
return unique
def save(self, *args, **kwargs):
self.href = slugify(self.name)
super(Categories,self).save(*args, **kwargs)
def get_absolute_url(self):
return self.href
class Post(models.Model):
authorId = models.ManyToManyField(Author)
title = models.CharField(max_length=255)
desc = models.CharField(max_length=255)
categoryID = models.ForeignKey(Categories,on_delete=models.CASCADE,)
content = models.TextField(blank=False)
href = models.SlugField(unique=True,editable=False)
draft = models.BooleanField(default=True)
readingtime = models.IntegerField(default=0)
updated = models.DateTimeField()
date = models.DateTimeField(editable=False,null=True)
published = models.DateField(default=timezone.now)
featuredimage = models.ImageField(null=True, blank=True, upload_to='post')
class Meta:
ordering = ['-date', '-updated']
def __str__(self):
return self.title
def get_slug(self):
slug = slugify(self.title.replace("ı","i"))
unique = slug
number = 1
while Post.objects.filter(slug=unique).exists():
unique = "{}-{}" .format(slug,number)
number += 1
return unique
##property
#def comments(self):
# instance = self
# qs = Comment.objects.filter_by_instance(instance)
# return qs
def get_markdown(self):
content = self.content
markdown_text = markdown(content)
return mark_safe(markdown_text)
def pre_save_receiver(sender, instance, *args, **kwargs):
if not instance.href and instance.title:
instance.href = slugify(instance.title)
if instance.content:
html_string = instance.get_markdown()
read_time_var = get_read_time(html_string)
instance.read_time = read_time_var
pre_save.connect(pre_save_receiver, sender=Post)
and this error
IntegrityError at /adminpost/author/add/
UNIQUE constraint failed: post_author.href
Request Method: POST
Request URL: http://127.0.0.1:8000/adminpost/author/add/
Django Version: 3.2.8
Exception Type: IntegrityError
Exception Value:
UNIQUE constraint failed: post_author.href
It seems that you try to make a create with a title that already exists. Thus your href field isn't unique anymore.
Either also make the title field unique or you can add some random number identifier to your slug.
import uuid
def pre_save_receiver(sender, instance, *args, **kwargs):
if not instance.href and instance.title:
if not Post.objects.filter(href=slugify(instance.title)).exists():
instance.href = slugify(instance.title)
else:
instance.href = slugify(instance.title) + str(uuid.uuid4())[:8]
I have been attempting to import data into my Django project using Django import-export. I have two models Ap and Job, Job has a FK relationship with Ap. Using the Admin, I can select the file and the type, CSV. So far my program seems to run, but gets hung up on the FK. I'm close, something is off and causing the import script to fail.
Models.py
class Ap(models.Model):
line_num = models.IntegerField()
vh = models.IntegerField()
vz = models.IntegerField()
status = models.CharField(
choices=statuses, default="select", max_length=40)
classified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Job(models.Model):
aplink = models.ForeignKey(Ap, related_name=(
"job2ap"), on_delete=models.CASCADE)
job_num = models.IntegerField()
description = models.CharField(max_length=200)
category = models.CharField(
choices=categories, default="select", max_length=40)
status = models.CharField(
choices=statuses, default="select", max_length=40)
dcma = models.BooleanField(default=False),
due_date = models.DateField(blank=True),
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
views.py
class ImportView(View):
def get(self, request):
form = ImportForm()
return render(request, 'importdata.html', {'form': form})
def post(self, request):
form = ImportForm(request.POST, request.FILES)
job_resource = JobResource()
data_set = Dataset()
if form.is_valid():
file = request.FILES['import_file']
imported_data = data_set.load(file.read())
result = job_resource.import_data(
data_set, dry_run=True) # Test the data import
if not result.has_errors():
job_resource.import_data(
data_set, dry_run=False) # Actually import now
else:
form = ImportForm()
return render(request, 'importdata.html', {'form': form})
resource.py
class CharRequiredWidget(widgets.CharWidget):
def clean(self, value, row=None, *args, **kwargs):
val = super().clean(value)
if val:
return val
else:
raise ValueError('this field is required')
class ForeignkeyRequiredWidget(widgets.ForeignKeyWidget):
def clean(self, value, row=None, *args, **kwargs):
if value:
print(self.field, value)
return self.get_queryset(value, row, *args, **kwargs).get(**{self.field: value})
else:
raise ValueError(self.field + " required")
class JobResource(resources.ModelResource):
aplink = fields.Field(column_name='aplink', attribute='aplink', widget=ForeignkeyRequiredWidget(Ap,'id'),
saves_null_values=False)
job_num = fields.Field(saves_null_values=False, column_name='job_num', attribute='job_num',
widget=widgets.IntegerWidget())
description = fields.Field(column_name='description', attribute='description', saves_null_values=False,
widget=CharRequiredWidget())
class Meta:
model = Job
fields = ('aplink', 'job_num', 'description',)
clean_model_instances=True
admin.py
class JobResource(resources.ModelResource):
class Meta:
model=Job
fields=('aplink','job_num','description',)
class JobAdmin(ImportExportModelAdmin):
resource_class = JobResource
admin.site.register(Job, JobAdmin)
CSV file, data to import. I have tried leaving the first column empty, as will as putting the Id of the only Ap stored in the table ie 1. I have also tried hard coding the line_num, which is 1200 the first column as well.
CSV file for importing data:
Date importing errors:
In your resources, while defining fields, you need to include id field in the list. So change JobResource to the following:
class JobResource(resources.ModelResource):
class Meta:
model = Job
fields = ('id', 'aplink', 'job_num', 'description')
If you have defined a custom id field, then you will need to provide:
import_id_fields = ('your_id_field')
i have a question about django-models that how i can set field to unique=True of Table in django for each User. I have Table Token below, and when i added new token, it't auto create tokenprofile by signals django built-in, i want to unique uid of ProfileToken for each User. Show me how. Thanks
from django.db import models
from accounts.models import CustomeUser
from django.db.models.signals import post_save
import facebook
# Create your models here.
class Token(models.Model):
access_token = models.CharField(max_length=255)
user = models.ForeignKey(CustomeUser, related_name='tokens', on_delete=models.CASCADE)
def __str__(self):
return self.access_token
class TokenProfile(models.Model):
token = models.OneToOneField(Token, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
uid = models.CharField(max_length=50, unique=False)
profile_picture_link = models.CharField(max_length=255)
profile_link = models.CharField(max_length=255)
def __str__(self):
return '{0} - {1}'.format(self.name, self.uid)
def create_profile_token(sender, instance, created, **kwargs):
if created:
access_token = instance.access_token
graph = facebook.GraphAPI(access_token=access_token)
info = graph.get_object('me')
name = info['name']
uid = info['id']
profile_picture_link = 'https://graph.facebook.com/v3.1/{0}/picture?type=small&redirect=true'.format(uid)
profile_link = 'https://www.facebook.com/{0}'.format(uid)
profile_token = TokenProfile.objects.create(token=instance, name=name, uid=uid, profile_picture_link=profile_picture_link, profile_link=profile_link)
post_save.connect(create_profile_token, sender=Token)
I am using python ebay-sdk library for connection to ebay api.
I want to get some cases from ebay api and save it to the database.
I have one app and models like this:
class Case(models.Model):
# case is not necessarily a listing (often O2M)
STATUS = Choices('new', 'cancelled', 'active', 'taken_down', 'archived')
name = models.CharField(max_length=512)
when_created = models.DateTimeField(default=now, blank=True)
user = models.ForeignKey(User)
make = models.ForeignKey(Make)
status = StatusField(default=STATUS.new)
platform = models.ForeignKey(Platform, on_delete=models.CASCADE)
listing_id = models.CharField(max_length=64)
listing_owner = models.CharField(max_length=128)
url = models.CharField(max_length=512)
price = models.FloatField(blank=True, null=True)
# for now currency as char is ok.
currency = models.CharField(max_length=3, default='USD')
quantity = models.IntegerField(default=1)
when_listing_started = models.DateTimeField(blank=True, null=True)
when_listing_ends = models.DateTimeField(blank=True, null=True)
valid_days = models.IntegerField(blank=True, null=True)
objects = models.Manager.from_queryset(CaseQueryset)()
def __str__(self):
return self.name
#property
def days_valid(self):
created = self.when_created.replace(tzinfo=None)
now = datetime.datetime.utcnow().replace(tzinfo=None)
datetime.timedelta(6, 1)
return (now - created).days
def handle_sent_report(self):
self._set_active_state()
def handle_cancel(self):
self._set_cancelled_state()
def _set_active_state(self):
if self.status != Case.STATUS.new:
raise InvalidCaseStatus
self.status = Case.STATUS.active
self.save()
def _set_cancelled_state(self):
if self.status not in (Case.STATUS.new, Case.STATUS.active):
raise InvalidCaseStatus
self.status = Case.STATUS.cancelled
self.save()
And I created other app ebay-finder and ebay_find.py to find cars in eBay:
from ebaysdk.finding import Connection as Finding
from django.db import models
from cases.models import Case
from trademarks.models import Make
def ebay_find():
pass
api = Finding(domain='svcs.sandbox.ebay.com', appid="MY_EBAY_APP_ID", config_file=None)
response = api.execute('findItemsAdvanced', {'keywords': 'Cadillac'})
items = response.dict()
items_list = items['searchResult'].get('item')
ebay_cases = []
for item in items_list:
new_case = Case.objects.create(
name=item['title'],
platform="Ebay",
listing_id=car["model"],
url=item['viewItemURL'],
price=item['sellingStatus']['currentPrice']['value'],
currency=item['sellingStatus']['currentPrice']['_currencyId']
)
new_case.save()
I'm confused how to add this data from items_list to my database like in Case. I must take all the same fields like in Case? Can I have some fields default on create object?
If your want your data to be saved with the least hassle, then yes, use the same field-names.
You have an example of a model field with a default in your own code:
currency = models.CharField(max_length=3, default='USD')
You could also override your model's save() method:
def save(self, *args, **kwargs):
super(Case, self).save(*args, **kwargs)
if not self.when_listing_started:
self.when_listing_started = datetime.datetime.now()
super(Case, self).save(*args, **kwargs)
I know in templates you can easily count comments with get_comment_count, but how can you get the same count as a method inside a class in models.py?
For example
class Blog( models.Model ) :
def comment_count( self ) :
return self.comment_set.count() # This line won't work.
title = ....
....
I want to have comment_count so that I can get a count the comments in the admin page.
EDIT: For your convenience, here is the models.py for django.contrib.comments
from django.contrib.auth.models import User
from django.contrib.comments.managers import CommentManager
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.db import models
from django.core import urlresolvers
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.conf import settings
COMMENT_MAX_LENGTH = getattr(settings,'COMMENT_MAX_LENGTH',3000)
class BaseCommentAbstractModel(models.Model):
"""
An abstract base class that any custom comment models probably should
subclass.
"""
# Content-object field
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
# Metadata about the comment
site = models.ForeignKey(Site)
class Meta:
abstract = True
def get_content_object_url(self):
"""
Get a URL suitable for redirecting to the content object.
"""
return urlresolvers.reverse(
"comments-url-redirect",
args=(self.content_type_id, self.object_pk)
)
class Comment(BaseCommentAbstractModel):
"""
A user comment about some object.
"""
# Who posted this comment? If ``user`` is set then it was an authenticated
# user; otherwise at least user_name should have been set and the comment
# was posted by a non-authenticated user.
user = models.ForeignKey(User, verbose_name=_('user'),
blank=True, null=True, related_name="%(class)s_comments")
user_name = models.CharField(_("user's name"), max_length=50, blank=True)
user_email = models.EmailField(_("user's email address"), blank=True)
user_url = models.URLField(_("user's URL"), blank=True)
comment = models.TextField(_('comment'), max_length=COMMENT_MAX_LENGTH)
# Metadata about the comment
submit_date = models.DateTimeField(_('date/time submitted'), default=None)
ip_address = models.IPAddressField(_('IP address'), blank=True, null=True)
is_public = models.BooleanField(_('is public'), default=True,
help_text=_('Uncheck this box to make the comment effectively ' \
'disappear from the site.'))
is_removed = models.BooleanField(_('is removed'), default=False,
help_text=_('Check this box if the comment is inappropriate. ' \
'A "This comment has been removed" message will ' \
'be displayed instead.'))
# Manager
objects = CommentManager()
class Meta:
db_table = "django_comments"
ordering = ('submit_date',)
permissions = [("can_moderate", "Can moderate comments")]
verbose_name = _('comment')
verbose_name_plural = _('comments')
def __unicode__(self):
return "%s: %s..." % (self.name, self.comment[:50])
def save(self, *args, **kwargs):
if self.submit_date is None:
self.submit_date = timezone.now()
super(Comment, self).save(*args, **kwargs)
def _get_userinfo(self):
"""
Get a dictionary that pulls together information about the poster
safely for both authenticated and non-authenticated comments.
This dict will have ``name``, ``email``, and ``url`` fields.
"""
if not hasattr(self, "_userinfo"):
self._userinfo = {
"name" : self.user_name,
"email" : self.user_email,
"url" : self.user_url
}
if self.user_id:
u = self.user
if u.email:
self._userinfo["email"] = u.email
# If the user has a full name, use that for the user name.
# However, a given user_name overrides the raw user.username,
# so only use that if this comment has no associated name.
if u.get_full_name():
self._userinfo["name"] = self.user.get_full_name()
elif not self.user_name:
self._userinfo["name"] = u.username
return self._userinfo
userinfo = property(_get_userinfo, doc=_get_userinfo.__doc__)
def _get_name(self):
return self.userinfo["name"]
def _set_name(self, val):
if self.user_id:
raise AttributeError(_("This comment was posted by an authenticated "\
"user and thus the name is read-only."))
self.user_name = val
name = property(_get_name, _set_name, doc="The name of the user who posted this comment")
def _get_email(self):
return self.userinfo["email"]
def _set_email(self, val):
if self.user_id:
raise AttributeError(_("This comment was posted by an authenticated "\
"user and thus the email is read-only."))
self.user_email = val
email = property(_get_email, _set_email, doc="The email of the user who posted this comment")
def _get_url(self):
return self.userinfo["url"]
def _set_url(self, val):
self.user_url = val
url = property(_get_url, _set_url, doc="The URL given by the user who posted this comment")
def get_absolute_url(self, anchor_pattern="#c%(id)s"):
return self.get_content_object_url() + (anchor_pattern % self.__dict__)
def get_as_text(self):
"""
Return this comment as plain text. Useful for emails.
"""
d = {
'user': self.user or self.name,
'date': self.submit_date,
'comment': self.comment,
'domain': self.site.domain,
'url': self.get_absolute_url()
}
return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d
class CommentFlag(models.Model):
"""
Records a flag on a comment. This is intentionally flexible; right now, a
flag could be:
* A "removal suggestion" -- where a user suggests a comment for (potential) removal.
* A "moderator deletion" -- used when a moderator deletes a comment.
You can (ab)use this model to add other flags, if needed. However, by
design users are only allowed to flag a comment with a given flag once;
if you want rating look elsewhere.
"""
user = models.ForeignKey(User, verbose_name=_('user'), related_name="comment_flags")
comment = models.ForeignKey(Comment, verbose_name=_('comment'), related_name="flags")
flag = models.CharField(_('flag'), max_length=30, db_index=True)
flag_date = models.DateTimeField(_('date'), default=None)
# Constants for flag types
SUGGEST_REMOVAL = "removal suggestion"
MODERATOR_DELETION = "moderator deletion"
MODERATOR_APPROVAL = "moderator approval"
class Meta:
db_table = 'django_comment_flags'
unique_together = [('user', 'comment', 'flag')]
verbose_name = _('comment flag')
verbose_name_plural = _('comment flags')
def __unicode__(self):
return "%s flag of comment ID %s by %s" % \
(self.flag, self.comment_id, self.user.username)
def save(self, *args, **kwargs):
if self.flag_date is None:
self.flag_date = timezone.now()
super(CommentFlag, self).save(*args, **kwargs)
Comments are related to your Models via generic relations so you can look up the comments for your object as you would any generic relation:
from django.conrtib.comments.models import Comment
from django.contrib.contenttypes.models import ContentType
class Blog( models.Model ) :
def comment_count(self) :
ct = ContentType.objects.get_for_model(Blog)
obj_pk = self.id
return Comment.objects.filter(content_type=ct,object_pk=obj_pk).count()