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)
Related
I am building an API for a game and need to select a random game round for a randomly chosen resource.
Choosing the random resource works. What I am trying to do now, in order to coordinate players is to filter the game rounds by the resource that has been chosen randomly and then return a random game round.
The code below shows what I have tried so far, namely to access the resource for which a game round has been played over the method with the #property decorator.
models.py
class Resource(models.Model):
id = models.PositiveIntegerField(null=False, primary_key=True)
hash_id = models.CharField(max_length=256)
creators = models.ManyToManyField(Creator)
titles = models.ManyToManyField(Title)
created_start = models.DateField(null=True)
created_end = models.DateField(null=True)
location = models.CharField(max_length=512, null=True)
institution_source = models.CharField(max_length=512, blank=True)
institution = models.CharField(max_length=512, blank=True)
origin = models.URLField(max_length=256, null=True)
enabled = models.BooleanField(default=True)
media_type = models.CharField(max_length=256, default='picture')
objects = models.Manager()
def __str__(self):
return self.hash_id or ''
#property
def tags(self):
tags = self.taggings.values('tag').annotate(count=Count('tag'))
return tags.values('tag_id', 'tag__name', 'tag__language', 'count')
class Gameround(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gamesession = models.ForeignKey(Gamesession, on_delete=models.CASCADE)
created = models.DateTimeField(editable=False)
score = models.PositiveIntegerField(default=0)
objects = models.Manager()
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
return super().save(*args, **kwargs)
def create(self):
pass
#property
def tags(self):
tags = self.taggings.values('tag')
return tags.values('tag_id', 'tag__name', 'tag__language', 'resource_id')
class Tagging(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
created = models.DateTimeField(editable=False)
score = models.PositiveIntegerField(default=0)
origin = models.URLField(max_length=256, blank=True, default='')
objects = models.Manager()
def create(self, tag):
tagging = self.create(tag=tag)
def __str__(self):
return str(self.tag) or ''
def save(self, *args, **kwargs):
if not self.id:
self.created = timezone.now()
return super().save(*args, **kwargs)
I am then serialising the game round like this:
*serializers.py
class GameroundSerializer(serializers.ModelSerializer):
gamesession = GamesessionSerializer(read_only=True)
user = CustomUserSerializer(read_only=True)
tags_to_compare = serializers.SerializerMethodField('get_tags_to_compare')
class Meta:
model = Gameround
fields = ['id', 'user', 'gamesession', 'created', 'score', 'tags_to_compare']
def get_tags_to_compare(self, round):
taggings = round.tags
return taggings
def to_representation(self, data):
data = super().to_representation(data)
return data
In the view I have a get request which first chooses a random resource, then I am trying to filter the game rounds to only include the ones where this resource was involved and then I am trying to retrieve a random game round.
views.py
class GameroundView(APIView):
"""
API endpoint for game rounds
"""
def get(self, request, *args, **kwargs):
controller = GameViewController()
random_resource = controller.get_resource()
gameround = Gameround.objects.all().filter(taggings__resource_id=random_resource).order_by("?").first()
gameround_serializer = GameroundSerializer(gameround)
return Response(gameround_serializer.data)
This does not seem to be working since I keep getting the error "The response content must be rendered before it can be iterated over."
I have also tried creating an id variable and passing it on as the resource_id like this:
# id of the random Resource for the game round
random_resource_id = random_resource.get(random_resource.id)
gameround = Gameround.objects.all().filter(taggings__resource_id=random_resource_id).order_by("?").first()
This leads to the 'Response' object has no attribute 'id' error when I reload the page in the browser.
Does anyone know how I can solve this issue?
Thank you in advance!
Below also the Gameviewcontroller that is inside the views.py file
class GameViewController:
def get_random_object(self, MyModel):
random_object = None
object_count = MyModel.objects.all().count() + 1
while not MyModel.objects.all().filter(id=random_object).exists():
for obj in range(object_count):
n = random.randint(1, object_count)
if MyModel.objects.all().filter(id=n).exists():
random_object = n
return random_object
def get_random_id(self, MyModel):
"""Method for large querysets"""
random_object = None
object_count = MyModel.objects.all().count() + 1
for obj in range(object_count):
n = random.randint(1, object_count)
while not MyModel.objects.all().filter(id=n).exists():
alt = random.randint(1, object_count)
if MyModel.objects.all().filter(id=alt).exists():
random_object = n
return random_object
def get_resource(self):
random_resource_id = self.get_random_object(Resource)
current_resource = Resource.objects.all().filter(id=random_resource_id)
resource_serializer = ResourceSerializer(current_resource, many=True)
data = {'resource': resource_serializer.data}
return Response(data, status=status.HTTP_200_OK)
def get_gameround_matching_resource(self, random_resource):
"""Checks if a previously played game round exists for a randomly chosen resource"""
current_gameround = Gameround.objects.all().filter(ressource__in=random_resource).order_by('?').first()
if current_gameround.exists():
gameround_serializer = GameroundSerializer(current_gameround)
data = {'gameround': gameround_serializer.data}
return Response(data, status=status.HTTP_200_OK)
def timer(self, start):
"""Start a new timer as soon as a gameround has been selected"""
start_time = start
elapsed_time = None
if start_time is not None:
start_time = time.perf_counter()
"""Stop the timer, and return the elapsed time"""
if start_time is None:
elapsed_time = time.perf_counter() - start_time
return elapsed_time
def get_gamesession(self):
"""Retrieves a randomly chosen gamesession object"""
current_gamesession = Gamesession.objects.all().order_by('?').first()
gamesession_serializer = GamesessionSerializer(current_gamesession)
data = {'gameround': gamesession_serializer.data}
return Response(data, status=status.HTTP_200_OK)
I finally managed to solve it by rewriting the get request like this:
def get(self, request, *args, **kwargs):
random_resource = Resource.objects.all().order_by('?').first()
resource_serializer = ResourceSerializer(random_resource) # Response is a serialized JSON object
random_resource_id = random_resource.id # id of the random Resource for the game round
gameround = Gameround.objects.all().filter(taggings__resource_id=random_resource_id).order_by("?").first()
gameround_serializer = GameroundSerializer(gameround)
return Response({
'resource to coordinate': resource_serializer.data,
'gameround': gameround_serializer.data,
})
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')
So i have this two models in django:
class Course(models.Model):
def get_image_path(self, filename):
return os.path.join('courses', str(self.slug), filename)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Course, self).save(*args, **kwargs)
name = models.CharField(max_length=255, verbose_name="Nombre")
description = models.CharField(max_length=255, verbose_name="DescripciĆ³n")
price = models.DecimalField(max_digits=12,decimal_places=2, verbose_name="Precio")
slug = models.SlugField(blank=True, max_length=255)
icon_img = models.ImageField(upload_to=get_image_path, blank=True, null=True, verbose_name="Imagen")
background_color = ColorField(default="#026085")
class Meta:
verbose_name = "curso"
verbose_name_plural = "cursos"
class UserCourse(models.Model):
user = models.ForeignKey(User)
course = models.ForeignKey(Course)
So whenever a user "buys" a course, it is stored in UserCourse. I have a view where the system shows a list of all the courses the user has bought. This is the view code:
def user_course_list_view(request, username):
context_dict = {}
try:
user_courses = UserCourse.objects.filter(user=request.user).course_set
context_dict['courses'] = user_courses
context_dict['heading'] = "Mis cursos"
except:
context_dict['courses'] = None
context_dict['heading'] = "Mis cursos wey"
return render(request, 'courses/course_list.html', context=context_dict)
I dont know where is the error and I cant seem to catch the exception (im using django with docker)
tl;dr
Something like this should work.
usercourse_objects = UserCourse.objects.filter(user=request.user).select_related('course')
user_courses = [x.course for x in usercourse_objects]
Explanation
There are multiple ways to do this, but one way would be to first get all the UserCourse objects for the current user:
usercourse_objects = UserCourse.objects.filter(user=request.user)
Then, for each UserCourse object, get the related Course:
user_courses = [x.course for x in usercourse_objects]
Now, the second line causes N database queries (one for each time we follow the course foreign key relation. To prevent this, the first line can be changed to:
usercourse_objects = UserCourse.objects.filter(user=request.user).select_related('course')
This pre-populates the course attribute of the UserCourse objects. More info about select_related() can be found here.
It's a sort of cms type application
I have an article model and some specializations in models.py
class Article(models.Model):
foo = models.CharField(max_length=50)
bar = models.CharField(max_length=100,blank=True)
DISPLAY_CHOICES = (
('N', 'None'),
('C','Carousel'),
('M','Marketing'),
('F','Featurette')
)
display = models.CharField(max_length=1, choices = DISPLAY_CHOICES)
def __unicode__(self):
return self.title
class Artist(Article):
website = models.URLField(max_length=200,blank=True)
class Venue(Article):
location = models.CharField(max_length=150)
map_link = models.URLField(max_length=200,blank=True)
class Event(Article):
time = models.DateTimeField()
venue = models.ForeignKey(Venue)
performers = models.ManyToManyField(Artist)
I want to render these in different ways depending on the value of article.display but when I call
articles.objects.all()
I still need the extra attributes form the subclasses so I wrote
#views.py
def castToSubClass(article):
try:
return Artist.objects.get(article_ptr_id = article.id)
except:
try:
return Event.objects.get(article_ptr_id = article.id)
except:
try:
return Venue.objects.get(article_ptr_id = article.id)
except:
return article
def index(request):
carousel = [castToSubClass(article) for article in Article.objects.filter(display='C']
marketing = [castToSubClass(article) for article in Article.objects.filter(display='M'[:3]]
featurettes = [castToSubClass(article) for article in Article.objects.filter(display='F']
return render_to_response('frontpage.html',
{
'carousel': carousel,
'marketing':marketing,
'featurettes': featurettes
})
to turn them all in the appropriate subclass object, this apart from seeming clunky seems to mean I'm hitting the database twice for every (or nearly every) item in the queryset.
Is there a way to do this in the initial calls to the manager instead?
Thanks.
Use one model to store everything, and add a field to distinguish the article type, so that you can render different look for every type combine with display in the template(Like tumblr do).
class Article(models.Model):
foo = models.CharField(max_length=50)
bar = models.CharField(max_length=100,blank=True)
DISPLAY_CHOICES = (
('N', 'None'),
('C','Carousel'),
('M','Marketing'),
('F','Featurette')
)
display = models.CharField(max_length=1, choices = DISPLAY_CHOICES)
ARTICLE_TYPE_CHOICES = (
('artist', 'Artist'),
('venue', 'Venue'),
('event', 'Event'),
)
type = models.CharField(max_length=32, choices = ARTICLE_TYPE_CHOICES)
website = models.URLField(max_length=200,blank=True, null=True)
location = models.CharField(max_length=150, blank=True, null=True)
map_link = models.URLField(max_length=200,blank=True, null=True)
time = models.DateTimeField(null=True)
venue = models.ForeignKey('self', null=True)
performers = models.ManyToManyField('self', null=True)
def __unicode__(self):
return self.title
#views.py
def index(request):
carousel = Article.objects.filter(display='C')
marketing = Article.objects.filter(display='M')
featurettes = Article.objects.filter(display='F')
return render_to_response('frontpage.html',{'carousel': carousel, 'marketing':marketing, 'featurettes': featurettes})
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.