[PATCH 6/9] py3: Fix Django models Python3 compatibility

Stephen Finucane stephen.finucane at intel.com
Mon Nov 30 09:10:47 AEDT 2015


Per suggestions from the Django porting guide:

    https://docs.djangoproject.com/en/1.8/topics/python3/

Signed-off-by: Stephen Finucane <stephen.finucane at intel.com>
---
 patchwork/admin.py  |  2 +-
 patchwork/models.py | 74 ++++++++++++++++++++++++++++++++---------------------
 2 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/patchwork/admin.py b/patchwork/admin.py
index bada5a6..082885b 100644
--- a/patchwork/admin.py
+++ b/patchwork/admin.py
@@ -31,7 +31,7 @@ admin.site.register(Project, ProjectAdmin)
 
 
 class PersonAdmin(admin.ModelAdmin):
-    list_display = ('__unicode__', 'has_account')
+    list_display = ('__str__', 'has_account')
     search_fields = ('name', 'email')
 
     def has_account(self, person):
diff --git a/patchwork/models.py b/patchwork/models.py
index a2b9498..ecfd986 100644
--- a/patchwork/models.py
+++ b/patchwork/models.py
@@ -18,6 +18,8 @@
 # along with Patchwork; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+from __future__ import absolute_import
+
 from collections import Counter, OrderedDict
 import datetime
 import random
@@ -29,31 +31,37 @@ from django.contrib.sites.models import Site
 from django.core.urlresolvers import reverse
 from django.db import models
 from django.db.models import Q
+from django.utils.encoding import python_2_unicode_compatible
 from django.utils.functional import cached_property
+from django.utils import six
+from django.utils.six import add_metaclass
+from django.utils.six.moves import filter
 
 from patchwork.parser import extract_tags, hash_patch
 
 
+ at python_2_unicode_compatible
 class Person(models.Model):
     email = models.CharField(max_length=255, unique=True)
     name = models.CharField(max_length=255, null=True, blank=True)
     user = models.ForeignKey(User, null=True, blank=True,
                              on_delete=models.SET_NULL)
 
-    def __unicode__(self):
-        if self.name:
-            return u'%s <%s>' % (self.name, self.email)
-        else:
-            return self.email
-
     def link_to_user(self, user):
         self.name = user.profile.name()
         self.user = user
 
+    def __str__(self):
+        if self.name:
+            return '%s <%s>' % (self.name, self.email)
+        else:
+            return self.email
+
     class Meta:
         verbose_name_plural = 'People'
 
 
+ at python_2_unicode_compatible
 class Project(models.Model):
     linkname = models.CharField(max_length=255, unique=True)
     name = models.CharField(max_length=255, unique=True)
@@ -65,9 +73,6 @@ class Project(models.Model):
     send_notifications = models.BooleanField(default=False)
     use_tags = models.BooleanField(default=True)
 
-    def __unicode__(self):
-        return self.name
-
     def is_editable(self, user):
         if not user.is_authenticated():
             return False
@@ -79,10 +84,14 @@ class Project(models.Model):
             return []
         return list(Tag.objects.all())
 
+    def __str__(self):
+        return self.name
+
     class Meta:
         ordering = ['linkname']
 
 
+ at python_2_unicode_compatible
 class UserProfile(models.Model):
     user = models.OneToOneField(User, unique=True, related_name='profile')
     primary_project = models.ForeignKey(Project, null=True, blank=True)
@@ -98,8 +107,9 @@ class UserProfile(models.Model):
 
     def name(self):
         if self.user.first_name or self.user.last_name:
-            names = filter(bool, [self.user.first_name, self.user.last_name])
-            return u' '.join(names)
+            names = list(filter(
+                bool, [self.user.first_name, self.user.last_name]))
+            return ' '.join(names)
         return self.user.username
 
     def contributor_projects(self):
@@ -126,7 +136,7 @@ class UserProfile(models.Model):
                 action_required=True).values('pk').query)
         return qs
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name()
 
 
@@ -140,20 +150,21 @@ def _user_saved_callback(sender, created, instance, **kwargs):
 models.signals.post_save.connect(_user_saved_callback, sender=User)
 
 
+ at python_2_unicode_compatible
 class State(models.Model):
     name = models.CharField(max_length=100)
     ordering = models.IntegerField(unique=True)
     action_required = models.BooleanField(default=True)
 
-    def __unicode__(self):
+    def __str__(self):
         return self.name
 
     class Meta:
         ordering = ['ordering']
 
 
+ at add_metaclass(models.SubfieldBase)
 class HashField(models.CharField):
-    __metaclass__ = models.SubfieldBase
 
     def __init__(self, algorithm='sha1', *args, **kwargs):
         self.algorithm = algorithm
@@ -161,13 +172,15 @@ class HashField(models.CharField):
             import hashlib
 
             def _construct(string=''):
+                if isinstance(string, six.text_type):
+                    string = string.encode('utf-8')
                 return hashlib.new(self.algorithm, string)
             self.construct = _construct
             self.n_bytes = len(hashlib.new(self.algorithm).hexdigest())
         except ImportError:
             modules = {'sha1': 'sha', 'md5': 'md5'}
 
-            if algorithm not in modules.keys():
+            if algorithm not in modules:
                 raise NameError("Unknown algorithm '%s'" % algorithm)
 
             self.construct = __import__(modules[algorithm]).new
@@ -181,6 +194,7 @@ class HashField(models.CharField):
         return 'char(%d)' % self.n_bytes
 
 
+ at python_2_unicode_compatible
 class Tag(models.Model):
     name = models.CharField(max_length=20)
     pattern = models.CharField(
@@ -191,13 +205,13 @@ class Tag(models.Model):
         max_length=2, unique=True, help_text='Short (one-or-two letter)'
         ' abbreviation for the tag, used in table column headers')
 
-    def __unicode__(self):
-        return self.name
-
     @property
     def attr_name(self):
         return 'tag_%d_count' % self.id
 
+    def __str__(self):
+        return self.name
+
     class Meta:
         ordering = ['abbrev']
 
@@ -249,6 +263,7 @@ class PatchManager(models.Manager):
         return self.get_queryset().with_tag_counts(project)
 
 
+ at python_2_unicode_compatible
 class Patch(models.Model):
     project = models.ForeignKey(Project)
     msgid = models.CharField(max_length=255)
@@ -267,9 +282,6 @@ class Patch(models.Model):
 
     objects = PatchManager()
 
-    def __unicode__(self):
-        return self.name
-
     def commit_message(self):
         """Retrieve the commit message."""
         return Comment.objects.filter(patch=self, msgid=self.msgid)
@@ -379,7 +391,7 @@ class Patch(models.Model):
 
             unique[ctx] = check
 
-        return unique.values()
+        return list(unique.values())
 
     @property
     def check_count(self):
@@ -403,6 +415,9 @@ class Patch(models.Model):
     def get_absolute_url(self):
         return ('patchwork.views.patch.patch', (), {'patch_id': self.id})
 
+    def __str__(self):
+        return self.name
+
     class Meta:
         verbose_name_plural = 'Patches'
         ordering = ['date']
@@ -469,9 +484,6 @@ class Bundle(models.Model):
                                         order=max_order + 1)
         bp.save()
 
-    class Meta:
-        unique_together = [('owner', 'name')]
-
     def public_url(self):
         if not self.public:
             return None
@@ -490,6 +502,9 @@ class Bundle(models.Model):
             'bundlename': self.name,
         })
 
+    class Meta:
+        unique_together = [('owner', 'name')]
+
 
 class BundlePatch(models.Model):
     patch = models.ForeignKey(Patch)
@@ -542,7 +557,7 @@ class Check(models.Model):
             self.id, self.context, self.get_state_display())
 
     def __unicode__(self):
-        return ('%s (%s)' % (self.context, self.get_state_display()))
+        return '%s (%s)' % (self.context, self.get_state_display())
 
 
 class EmailConfirmation(models.Model):
@@ -573,17 +588,18 @@ class EmailConfirmation(models.Model):
         super(EmailConfirmation, self).save()
 
 
+ at python_2_unicode_compatible
 class EmailOptout(models.Model):
     email = models.CharField(max_length=200, primary_key=True)
 
-    def __unicode__(self):
-        return self.email
-
     @classmethod
     def is_optout(cls, email):
         email = email.lower().strip()
         return cls.objects.filter(email=email).count() > 0
 
+    def __str__(self):
+        return self.email
+
 
 class PatchChangeNotification(models.Model):
     patch = models.OneToOneField(Patch, primary_key=True)
-- 
2.0.0



More information about the Patchwork mailing list