Why is a Bad Idea to inherit the Product model in Satchmo (and how to do it right)

I've seen on some articles on the web that one of the ways people is extending the Product model in Satchmo is inheriting from it.

I used it once and it was a Bad Idea, and I will explain why. With a little luck Google will index this and show this to people looking for ways to override Product because currently if one looks at the results it's like that inheriting Product is the only way.

Probably one of the reasons that people do this is because in Django is very common to extend the django.auth User model inheriting it. But this case is different, because:

  • Django doesn't ship with default templates
  • The User model in Django is only used on a few default views where only the User fields have sense.
  • The User mode in Django is not linked to any other Django default models.
  • Inheriting User allows for cleaner implementations when you have lots of different user types and roles; using Profiles you would have to implement it using an intermediate profile which for every type of user links to the real profile.

Inheriting Product is a bad idea in Satchmo because:

  • Satchmo ships with lots of default Templates. Usually most sites will rewrite some of these templates, but...
  • Those templates are generated by lots of default views. Most of those use Product or some object linked with Product, so the templates will receive objects that only have Product fields, even if they were generated using your custom model. So you'll be unable to access any field of your model inside the template, except rewriting the views (and the urls pointing to the views.)
  • There are very important models like Order, OrderItem, CartItem and others linked to the Product model. So you'll have to inherit those two, along with rewriting (again!) the templates and views that use them.
  • You don't gain anything by inheriting Product compared with the Satchmo standard way.

So what is my proposal for extending a Product object? Easy, just use what Satchmo documentation propose.

For example if we want to extend our shop products adding the "code" and "provider" fields we can follow these precise steps:

On your core app models.py, add the extension model (take care to don't call it "Product" or it will clash with Satchmo product model!):

from django.db import models
from django.utils.translation import ugettext_lazy as _

import django.db.models.signals as djangosignals
from product.models import Product
from satchmo_store.contact.models import Organization

class LocalProduct(models.Model):
    product = models.OneToOneField(Product, verbose_name=_('Base Product'))

    code = models.CharField(verbose_name=_('Code'), max_length=32, blank=True, null=True)
    suplier = models.ManyToManyField(Organization, verbose_name=_('Supplier'), blank=True, null=True)

    def _get_subtype(self):  return 'LocalProduct'

    def __unicode__(self):
        if self.code:
            return u"%s [%s]" % (self.product.name, self.code)
        else:
            return u'%s' % self.product.name

    class Admin: pass

    class Meta:
        verbose_name = _('LocalProduct')
        verbose_name_plural = _('LocalProducts')

import config

Add this on your core app config.py (change "localsite" for your app name):

from django.utils.translation import ugettext_lazy as _
from livesettings import config_get

PRODUCT_TYPES = config_get('PRODUCT', 'PRODUCT_TYPES')
PRODUCT_TYPES.add_choice(('localsite::LocalProduct', _('Local Product')))

If you want the LocalProduct to show on the admin automatically linked when you add or edit a product (hint: you do) then add this to you app admin.py, configuring the admin display fields to your taste:

from django.contrib import admin
from product.models import Product
from models import LocalProduct
from livesettings import config_value
from product.admin import ProductAttribute_Inline, Price_Inline, \
    ProductImage_Inline, ProductTranslation_Inline, ProductOptions

class LocalProduct_Inline(admin.StackedInline):
    model = LocalProduct
    extra = 1

class LocalProductOptions(ProductOptions):
    exclude = ('slug', 'sku', 'meta', 'length', 'length_units',
               'width', 'width_units', 'height', 'height_units',
               'weight_units', 'related_items', 'also_purchased',
               'date_added', 'taxable', 'taxClass', 'shipclass',
               'site',)

    fieldsets = (
        (None, {'fields': ('name', 'category', 'description', 
        'short_description', 'weight', 'active', 'featured', 
        'items_in_stock', 'total_sold','ordering',)}),
        )

    list_display = ('name', 'items_in_stock', 'unit_price')
    list_display_links = ('name',)
    search_fields = ['name', 'description', 'short_description']
    inlines = [LocalProduct_Inline, Price_Inline, ProductImage_Inline]
    filter_horizontal = ('category',)

    if config_value('LANGUAGE','SHOW_TRANSLATIONS'):
        inlines.append(ProductTranslation_Inline)

admin.site.unregister(Product)
admin.site.register(Product, LocalProductOptions)

Finally, if you want to have something done when the Product saves, instead of overriding save on an inheriting model save(), use Django signals:

models.py (at the end):

from satchmo_store.shop import get_satchmo_setting, signals
from listeners import *

djangosignals.pre_save.connect(product_pre_save, sender = Product)

listeners.py:

def product_pre_save(sender, instance, **kwargs):
    if instance.pk is None:
        instance.site = Site.objects.get(pk=1)
        instance.taxable = True
        instance.taxClass = TaxClass.objects.get(pk=1)
        instance.date_added = datetime.date.today()
        if not instance.has_full_weight:
            instance.weight = Decimal(0)
            instance.weight_units = 'grs'

And that's it. You have an extended Product without any of the pains. Now if you want to access some of the new fields from a template you just do: product.localproduct.code, without the need to overwrite any Satchmo view or inheriting lots of Product-related objects.

Compartir/Guardar/Bookmark

iPhone 3G Vs Android (en un T-Mobile G1/HTC Dream)

Desde junio de 2008 hasta febrero de 2009 tuve un iPhone 3G. Fue con diferencia el mejor teléfono móvil que había tenido hasta la fecha (mi primer smartphone propiamente dicho, si exceptuamos el Nokia N73 que no era muy smart.) Pero en febrero cambién a un móvil T-Mobile G1 conocido en Europa como el HTC Dream. El cambio (sin probar el móvil) fue motivado por:

  • No es de Apple: Apple es una compañía que hace casi siempre productos interesantes. Sin embargo su nivel de gilipollez general es altísimo. Por gilipollez general entiendo sus condiciones casi nazis para publicar aplicaciones, tener que pasar por la mierda infumable del iTunes para todo y las estupideces como no poner cosas básicas como MMS, cámara con video o cortar y pegar (eso ya lo han puesto en el iPhone 3G 2.0... dos años después de la salida del primer iPhone.)
  • Android: el Android me atraía como sistema para móviles desde que lo anunciaron. Multitarea, código abierto, personalizable sin hacks y basado en Linux. La única forma de que lo hubieran hecho más para mí sería que se programara en Python o D en lugar de Java. Además el hecho de que sea software abierto quiere decir que no sólo se ven mejoras hechas por cuatro hackers desperdigados sino por compañías importantes como HTC, Sony, Motorola y otras que se apuntaron al consorcio Open Handset Alliance. Y por supuesto también de los cuatro hackers, que también hacen firmwares y cosas interesantes.
  • Mejores características técnicas: más memoria RAM, mejor CPU.
  • Teclado físico: yo considero un teclado físico algo muy importante en un smartphone porque lo vamos a usar mucho para escribir correo, actualizar los tweets, el estado de Facebook... etc. El correo en pantalla del iPhone es bastante bueno, pero los teclado físicos están en otra categoría en cuanto a usabilidad.

El HTC Dream lo compré nuevo y libre en ebay por 300€ y vendí el iPhone por 390€, así que realmente salí ganando 90€ con el cambio.

En el artículo (pincha debajo para leerlo entero) voy a dar mi opinión categoría por categoría.

Continua dentro; pincha para leerlo completo (5348 palabras)

Vim: explicación de ventanas, pestañas y buffers

[Pincha aquí para ver otros artículos sobre Vim]

Cuando uno empieza a usar el potentísimo editor de texto Vim y consigue pasar de esa primera fase en la que nos horrorizamos de que todo sea tan distinto y poco intuitivo a la segunda fase en la que entendemos la filosofía de trabajo del editor (y es resto de editores nos empiezan a parecer poco intuitivos), es aún posible que nos líemos un poco con los conceptos de Vim de vista, ventana, buffer y pestaña.

Vamos a ver que es cada cosa y cómo suelen usarlas los usuarios avanzados de Vim, pero antes de explicarlo pongo un dibujito muy mono (pincha para ampliarlo mucho, es grande pero ocupa poco):

  • Vista: La vista en Vim es lo que vemos en el monitor. Una vista estará siempre dentro de una pestaña (si sólo tenemos una pestaña abierta la vista estará en la única pestaña) y a su vez puede contener una o más ventanas dentro.

  • Ventanas: En Vim una ventana es una subdivisión de la pantalla total que el programa puede usar. Es decir, si estás editando un fichero entero y éste ocupa toda la pantalla que ocupe el Vim, tienes una ventana. Si haces :split o :vsplit, tendrás ahora dos ventanas, viendo el mismo fichero (buffer). Si ahora en una de ellas haces de nuevo :split, tendrás tres ventanas. La utilidad de las ventanas es que nos permiten ver varios buffers o varias partes de un mismo buffer simultáneamente, lo cual en ocasiones es muy útil para programar. Como mínimo vas a tener siempre una ventana porque si cierras la última se cierra el programa y en cualquier momento estamos trabajando a través de una (o varias) ventanas. Incluso cuando estamos en una pestaña, realmente estamos en una ventana que está dentro de una pestaña.

  • Buffers: Los buffers son el concepto más importante de los de esta lista porque son los que contienen realmente los ficheros que abrimos, por lo que podemos pensar en los buffers como los ficheros cargados en memoria; si cerramos un buffer se cierra realmente el fichero y si abrimos un buffer se abre otro fichero. Esto no sucede con ventanas y pestañas porque cuando cerramos una pestaña a través de la cual estamos viendo un buffer el buffer sigue existiendo porque sólo hemos cerrado la ventana. Podemos hacer una metáfora diciendo que las ventanas son las ventanas de una casa y los buffers son las personas; si cierras la ventana la persona sigue estando detrás. La única excepción es cuando cerramos la última ventana abierta en la única pestaña que queda; en ese caso Vim considera que ya no vamos a trabajar más, dado que siempre trabajamos a través de una ventana, y por lo tanto cierra también todos los buffers que hubiera abiertos.

  • Pestañas: Las pestañas, una funcionalidad relativamente reciente de Vim permiten mantener simultaneamente varias vistas, cada una de las cuales puede contener una o más ventanas, y en esa única o múltiples pestañas podemos estar viendo uno o más buffers.

Compartir/Guardar/Bookmark

Porque uso KDE en Linux

Aquí está, copiando un directorio mediante SSH entre dos máquinas remotas en distintos continentes desde el gestor de ficheros. Sí, en otros sistemas se puede hacer instalando y configurando cosas, pero KDE lo hace por defecto, sin hacer absolutamente nada más.

Compartir/Guardar/Bookmark

Mini visión general y opinión sobre Django

Django es un en entorno de programación web que lleva varios años funcionando aunque ha sido ahora (2009) cuando ha visto un auge importante en su uso debido a la salida de la primera versión estable, la 1.0. En este artículo quiero dar una visión general y resumida de como está estructurado y como funciona, así como mi opinión personal-profesional. Es importante destacar que este artículo no es un curso ni un tutorial de Django, aunque sin duda tener unas nociones de como funciona Django en conjunto ayudará al que a continuación quiera aprenderlo a fondo.

[El artículo sigue en el interior]

Continua dentro; pincha para leerlo completo (2745 palabras)