2015-07-20

Abstract methods in Python3

You can use the Abstract Base Classes to implement abstract methods in Python. For example, if you have an Animal class and a Cat class like the following:
class Animal():
    def speak(self):
        print("Animal speak")

class Cat(Animal):
    pass

class Doc(Animal):
    pass

Cat().speak() # returns "Animal speak"
Dog().speak() # returns "Animal speak"
You can turn Animal.speak() to an abstract method with a metaclass and a decorator:
import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def speak(self):
        print("Animal speak")

class Cat(Animal):
    def speak(self):
        print("Cat speak")

class Dog(Animal):
    pass

Cat().speak() # returns "Cat speak"
Dog().speak() # returns "TypeError: Can't instantiate abstract class Dog with abstract methods speak"
Note that although the Animal.speak() method is always shadowed by its subclass method, it can still be called explicitly by using super():
class Cat(Animal):
    def speak(self):
        super().speak()

Cat().speak() # returns "Animal speak"

2015-07-13

Add a button to export the search results in Django - Part 2

Refering to a previous post, it turns out that using str() to serialize the QuerySet query is not a viable solution if one of the selection criteria is a date. The str() will not quote the date value resulting in an invalid SQL statement.
Using pickle to serialize the query solves the problem.
# Replace str()
request.session['search_sql'] = str(Person.objects.filter(**kw).query)

# with pickle.dumps()
request.session['search_query'] = pickle.dumps(Person.objects.filter(**kw).query)
When the time comes to re-execute the query:
# Rather than executing a raw sql statement:
sql = request.session.get('search_sql')
object_list = Person.objects.raw(sql)

# deserialize the saved query and assign it to the QuerySet instead:
saved_query = request.session.get('search_query')
query = pickle.loads(saved_query)
object_list = Person.objects.all()
object_list.query = query
Although less secured, in order to store the query object in the request session, have to use the pickle based session serializer instead of the JSON based counterpart in Django 1.7.
=== settings.py ===
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

2015-07-06

Set the width of an input field in an inline formset

To set the width of an input field in an inline formset, you can specify the widget and its attributes in the inlineformset_factory call. For example:
 LineFormSet = forms.models.inlineformset_factory(Document, DocumentLine, 
-                  fields=['seqno', 'sku', 'desc', 'qty', 'unit_price'])
+                  fields=['seqno', 'sku', 'desc', 'qty', 'unit_price'], 
+                  widgets={
+                    'seqno': forms.TextInput(attrs={'size':'10'}),
+                    'sku': forms.TextInput(attrs={'size':'10'}),
+                    'desc': forms.TextInput(attrs={'size':'50'}),
+                    'qty': forms.TextInput(attrs={'size':'10'}),
+                    'unit_price': forms.TextInput(attrs={'size':'10'})})
Reference: https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/#specifying-widgets-to-use-in-the-inline-form