hideki nara avatar hideki nara committed 631720e

csv_load and csv_dump for Model Manager

Comments (0)

Files changed (15)

 ^django_csvutils.egg-info/
 ^build/
 ^dist/
+app/*.log

csvutils/forms.py

         fieldnames.append(DELETION_FIELD_NAME)
 
     data = {}
-    reader = csv.DictReader(csvfile, fieldnames=fieldnames)
+    reader = csv.DictReader(csvfile )
     reader.reader = UnicodeReader(csvfile, encoding=encoding) 
     form_prefix = formset_cls.get_default_prefix()
     form_count = 0

csvutils/models.py

+from django.utils.encoding import force_unicode, smart_str
+from django.template.defaultfilters import capfirst
+from django.db.models.fields import FieldDoesNotExist
+
+from csvutils.http import CSVResponse
+from csvutils.utils import UnicodeWriter,UnicodeReader
+
+# - logger
+import logging,os
+logger= logging.getLogger(os.environ.get('APP_LOGGER','dev'))
+
+class CsvModelManager:
+
+    def from_dictrow(self,dictrow,keys=['id']):
+        ''' 
+        .. todo::
+            
+            - data sanitizing
+            - relation fields
+        '''
+        key_vals = dict([(k,v) for k,v in dictrow.items() if k in keys ] )
+        try:
+            rec = self.get(**key_vals)
+            try:
+                map(lambda k,v : setattr(rec,k,v), dictrow.items() )
+            except Exception,e: 
+                return rec,e
+        except self.model.DoesNotExist,e:
+            rec = self.model(**dictrow )
+        except Exception,e:
+            return None,e
+            
+        try:            
+            rec.save()
+            return rec ,None
+        except Exception,e:
+            return rec, e
+        
+    def csv_load(self,stream,encoding=None,keys=['id']):
+
+        for index,row in UnicodeReader.csv_enumerator(stream):
+            rec,e = self.from_dictrow(row,keys)
+            yield index,rec,e 
+
+    def csv_dump(self,fields=None, exclude=[], display=True, *args, **kwargs):
+        ''' 
+        .. todo::
+
+            - relation fields....
+            - format covnertion (e.g,datetime )
+        '''
+        # header
+        header = []
+        data_fields = fields if fields \
+                else [f.name for f in self.model._meta.fields if f.name not in exclude ]
+    
+        for field_name in data_fields:
+            col = field_name
+            header.append(col)
+
+        yield -1, header            #: -1 = hederline
+    
+        # body 
+        count=0
+        for obj in self.filter():
+            row = []
+            for field_name in data_fields:
+                try:
+                    field = self.model._meta.get_field(field_name)
+                    if display:
+                        if field.rel:
+                            rel_obj = getattr(obj, field.rel.related_name or field.name)
+                            val = force_unicode(rel_obj)
+                        else:
+                            val = obj._get_FIELD_display(field)
+                    else:
+                        val = getattr(obj, field.attname)
+                except FieldDoesNotExist:
+                    method = getattr(obj, field_name)
+                    val = method()
+                row.append(val)
+            count += 1
+            yield count,row

csvutils/shortcuts.py

     writer.writerows(data)
     return response
 
-def queryset_to_csv(queryset, fields=None, exclude=None, display=True, *args, **kwargs):
+def queryset_to_csv(queryset, fields=None, exclude=[], display=True, *args, **kwargs):
     model = queryset.model
     data = []
+
     # header
     header = []
-    if fields is None:
-        data_fields = []
-        for field in model._meta.fields:
-            if not exclude is None:
-                if field.attname in exclude:
-                    continue
-        data_fields.append(field.attname)
-    else:
-        data_fields = fields
+    data_fields = fields if fields \
+            else [f.attname for f in model._meta.fields if f.attrname not in exclude ]
 
     for field_name in data_fields:
         try:
         col = capfirst(col.replace('_', ' '))
         header.append(col)
     data.append(header)
+
     # body
     for obj in queryset:
         row = []

csvutils/utils.py

 
 from django.conf import settings
 from django.utils.encoding import force_unicode
+import pykf
 
 __all__ = ('UnicodeWriter', 'UnicodeReader')
 
         for row in rows:
             self.writerow(row)
 
+
+def detect_encoding(stream):
+    '''  Detect encodings.
+    .. todo:
+        - test other encodings than UTF-8/S-JIS 
+    '''
+    ret= {   
+        pykf.UTF8   : 'utf-8',
+        pykf.SJIS   : 'shift-jis',
+#        pykf.ASCII  : settings.DEFAULT_CHARSET, #: dirty fix : shift-jis
+    }.get( pykf.guess( stream.read() ),settings.DEFAULT_CHARSET)
+    stream.seek(0)
+    return ret
+
 class UnicodeReader(object):
     def __init__(self, stream, dialect=None, encoding=None, errors="strict", **kwds):
         self.reader = csv.reader(stream, dialect=dialect or csv.excel, **kwds)
-        self.encoding = encoding or settings.DEFAULT_CHARSET
+        self.encoding = encoding  if encoding else detect_encoding(stream)
         self.line_num = 0 # Needed for DictReader
         self.errors = errors
 
+pykf
+encutils

test/app/applogs.py

+#-*- coding: utf-8 -*-
+import os
+# - configure logging
+
+def config(LOGGING):
+    '''
+    >>> import applogs
+    >>> applogs.config(LOGGING)
+
+        - level 
+
+            DEBUG: Low level system information for debugging purposes
+            INFO: General system information
+            WARNING: Information describing a minor problem that has occurred.
+            ERROR: Information describing a major problem that has occurred.
+            CRITICAL: Information describing a critical problem that has occurred.
+    '''
+    #: custom formatter
+    if not LOGGING.has_key('formatters') : 
+        LOGGING['formatters']={} 
+        
+    LOGGING['formatters']['parsefriendly'] = { 
+        'format': '[%(levelname)s] %(asctime)s - M:%(module)s, P:%(process)d, T:%(thread)d, MSG:%(message)s',
+        'datefmt': '%d/%b/%Y:%H:%M:%S %z',
+    }
+
+    #  customer handler (console)
+    LOGGING['handlers']['console'] = { 
+        'level':    'DEBUG',
+        'class':    'logging.StreamHandler',
+    }
+
+    #  customer handler (file)
+    LOGGING['handlers']['file'] = { 
+        'level':    'DEBUG',
+        'class':    'logging.handlers.TimedRotatingFileHandler', 
+        'formatter':'parsefriendly',
+        'when':     'midnight',
+        'filename': os.environ.get('APP_LOGGER_FILE','app.log'),
+    }
+
+    #  customer logger (dev)
+    LOGGING['loggers']['dev'] = { 
+        'handlers': ['console'],
+        'level':    'DEBUG', 
+#        'level':    'CRITICAL', 
+        'propagete':    True,
+    }
+
+    #  customer logger (live)
+    LOGGING['loggers']['live'] = { 
+        'handlers': ['file'],
+#        'level':    'WARNING', 
+        'level':    'DEBUG', 
+        'propagete':    True,
+    }
+
+def get_logger():
+    ''' 
+    >>> import applogs
+    >>> logger = applogs.get_logger()
+    >>> logger.debug('An exception is raised')
+    '''
+    # - logging api 
+    import logging
+    return  logging.getLogger(os.environ.get('APP_LOGGER','dev'))
+

test/app/initial_data.json

+[
+  {
+    "pk": 22, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_logentry", 
+      "name": "Can add log entry", 
+      "content_type": 8
+    }
+  }, 
+  {
+    "pk": 23, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_logentry", 
+      "name": "Can change log entry", 
+      "content_type": 8
+    }
+  }, 
+  {
+    "pk": 24, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_logentry", 
+      "name": "Can delete log entry", 
+      "content_type": 8
+    }
+  }, 
+  {
+    "pk": 4, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_group", 
+      "name": "Can add group", 
+      "content_type": 2
+    }
+  }, 
+  {
+    "pk": 5, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_group", 
+      "name": "Can change group", 
+      "content_type": 2
+    }
+  }, 
+  {
+    "pk": 6, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_group", 
+      "name": "Can delete group", 
+      "content_type": 2
+    }
+  }, 
+  {
+    "pk": 10, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_message", 
+      "name": "Can add message", 
+      "content_type": 4
+    }
+  }, 
+  {
+    "pk": 11, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_message", 
+      "name": "Can change message", 
+      "content_type": 4
+    }
+  }, 
+  {
+    "pk": 12, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_message", 
+      "name": "Can delete message", 
+      "content_type": 4
+    }
+  }, 
+  {
+    "pk": 1, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_permission", 
+      "name": "Can add permission", 
+      "content_type": 1
+    }
+  }, 
+  {
+    "pk": 2, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_permission", 
+      "name": "Can change permission", 
+      "content_type": 1
+    }
+  }, 
+  {
+    "pk": 3, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_permission", 
+      "name": "Can delete permission", 
+      "content_type": 1
+    }
+  }, 
+  {
+    "pk": 7, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_user", 
+      "name": "Can add user", 
+      "content_type": 3
+    }
+  }, 
+  {
+    "pk": 8, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_user", 
+      "name": "Can change user", 
+      "content_type": 3
+    }
+  }, 
+  {
+    "pk": 9, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_user", 
+      "name": "Can delete user", 
+      "content_type": 3
+    }
+  }, 
+  {
+    "pk": 13, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_contenttype", 
+      "name": "Can add content type", 
+      "content_type": 5
+    }
+  }, 
+  {
+    "pk": 14, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_contenttype", 
+      "name": "Can change content type", 
+      "content_type": 5
+    }
+  }, 
+  {
+    "pk": 15, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_contenttype", 
+      "name": "Can delete content type", 
+      "content_type": 5
+    }
+  }, 
+  {
+    "pk": 28, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_feed", 
+      "name": "Can add feed", 
+      "content_type": 10
+    }
+  }, 
+  {
+    "pk": 29, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_feed", 
+      "name": "Can change feed", 
+      "content_type": 10
+    }
+  }, 
+  {
+    "pk": 30, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_feed", 
+      "name": "Can delete feed", 
+      "content_type": 10
+    }
+  }, 
+  {
+    "pk": 25, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_tag", 
+      "name": "Can add tag", 
+      "content_type": 9
+    }
+  }, 
+  {
+    "pk": 26, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_tag", 
+      "name": "Can change tag", 
+      "content_type": 9
+    }
+  }, 
+  {
+    "pk": 27, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_tag", 
+      "name": "Can delete tag", 
+      "content_type": 9
+    }
+  }, 
+  {
+    "pk": 16, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_session", 
+      "name": "Can add session", 
+      "content_type": 6
+    }
+  }, 
+  {
+    "pk": 17, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_session", 
+      "name": "Can change session", 
+      "content_type": 6
+    }
+  }, 
+  {
+    "pk": 18, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_session", 
+      "name": "Can delete session", 
+      "content_type": 6
+    }
+  }, 
+  {
+    "pk": 19, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "add_site", 
+      "name": "Can add site", 
+      "content_type": 7
+    }
+  }, 
+  {
+    "pk": 20, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "change_site", 
+      "name": "Can change site", 
+      "content_type": 7
+    }
+  }, 
+  {
+    "pk": 21, 
+    "model": "auth.permission", 
+    "fields": {
+      "codename": "delete_site", 
+      "name": "Can delete site", 
+      "content_type": 7
+    }
+  }, 
+  {
+    "pk": 1, 
+    "model": "auth.user", 
+    "fields": {
+      "username": "admin", 
+      "first_name": "", 
+      "last_name": "", 
+      "is_active": true, 
+      "is_superuser": true, 
+      "is_staff": true, 
+      "last_login": "2012-03-18 08:44:42", 
+      "groups": [], 
+      "user_permissions": [], 
+      "password": "sha1$94257$378e1ae6d19ab89e9d2c754d4a43e1c26fb76363", 
+      "email": "admin@admin.com", 
+      "date_joined": "2012-03-18 08:44:42"
+    }
+  }
+]

test/app/intial_data.json

-[
-  {
-    "pk": 22, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_logentry", 
-      "name": "Can add log entry", 
-      "content_type": 8
-    }
-  }, 
-  {
-    "pk": 23, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_logentry", 
-      "name": "Can change log entry", 
-      "content_type": 8
-    }
-  }, 
-  {
-    "pk": 24, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_logentry", 
-      "name": "Can delete log entry", 
-      "content_type": 8
-    }
-  }, 
-  {
-    "pk": 4, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_group", 
-      "name": "Can add group", 
-      "content_type": 2
-    }
-  }, 
-  {
-    "pk": 5, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_group", 
-      "name": "Can change group", 
-      "content_type": 2
-    }
-  }, 
-  {
-    "pk": 6, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_group", 
-      "name": "Can delete group", 
-      "content_type": 2
-    }
-  }, 
-  {
-    "pk": 10, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_message", 
-      "name": "Can add message", 
-      "content_type": 4
-    }
-  }, 
-  {
-    "pk": 11, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_message", 
-      "name": "Can change message", 
-      "content_type": 4
-    }
-  }, 
-  {
-    "pk": 12, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_message", 
-      "name": "Can delete message", 
-      "content_type": 4
-    }
-  }, 
-  {
-    "pk": 1, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_permission", 
-      "name": "Can add permission", 
-      "content_type": 1
-    }
-  }, 
-  {
-    "pk": 2, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_permission", 
-      "name": "Can change permission", 
-      "content_type": 1
-    }
-  }, 
-  {
-    "pk": 3, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_permission", 
-      "name": "Can delete permission", 
-      "content_type": 1
-    }
-  }, 
-  {
-    "pk": 7, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_user", 
-      "name": "Can add user", 
-      "content_type": 3
-    }
-  }, 
-  {
-    "pk": 8, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_user", 
-      "name": "Can change user", 
-      "content_type": 3
-    }
-  }, 
-  {
-    "pk": 9, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_user", 
-      "name": "Can delete user", 
-      "content_type": 3
-    }
-  }, 
-  {
-    "pk": 13, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_contenttype", 
-      "name": "Can add content type", 
-      "content_type": 5
-    }
-  }, 
-  {
-    "pk": 14, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_contenttype", 
-      "name": "Can change content type", 
-      "content_type": 5
-    }
-  }, 
-  {
-    "pk": 15, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_contenttype", 
-      "name": "Can delete content type", 
-      "content_type": 5
-    }
-  }, 
-  {
-    "pk": 25, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_feed", 
-      "name": "Can add feed", 
-      "content_type": 9
-    }
-  }, 
-  {
-    "pk": 26, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_feed", 
-      "name": "Can change feed", 
-      "content_type": 9
-    }
-  }, 
-  {
-    "pk": 27, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_feed", 
-      "name": "Can delete feed", 
-      "content_type": 9
-    }
-  }, 
-  {
-    "pk": 16, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_session", 
-      "name": "Can add session", 
-      "content_type": 6
-    }
-  }, 
-  {
-    "pk": 17, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_session", 
-      "name": "Can change session", 
-      "content_type": 6
-    }
-  }, 
-  {
-    "pk": 18, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_session", 
-      "name": "Can delete session", 
-      "content_type": 6
-    }
-  }, 
-  {
-    "pk": 19, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "add_site", 
-      "name": "Can add site", 
-      "content_type": 7
-    }
-  }, 
-  {
-    "pk": 20, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "change_site", 
-      "name": "Can change site", 
-      "content_type": 7
-    }
-  }, 
-  {
-    "pk": 21, 
-    "model": "auth.permission", 
-    "fields": {
-      "codename": "delete_site", 
-      "name": "Can delete site", 
-      "content_type": 7
-    }
-  }, 
-  {
-    "pk": 1, 
-    "model": "auth.user", 
-    "fields": {
-      "username": "admin", 
-      "first_name": "", 
-      "last_name": "", 
-      "is_active": true, 
-      "is_superuser": true, 
-      "is_staff": true, 
-      "last_login": "2012-01-23 16:15:52", 
-      "groups": [], 
-      "user_permissions": [], 
-      "password": "sha1$468b0$ef8f87e580d0e8f2ba9d3d920af842bc1e41f5d6", 
-      "email": "admin@admin.com", 
-      "date_joined": "2012-01-23 16:15:52"
-    }
-  }
-]

test/app/requirements.txt

+feedparser

test/app/sample/fixtures/dump.csv

+id,link,title,type
+1,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/world/taiwan/,平沼氏、台湾に献花問題謝罪,
+2,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/domestic/solitary_death/,母子死亡 夏から息子通所せず,
+3,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/economy/aij/,AIJ社長 実態と逆の「哲学」,
+4,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/world/germany/,ナチス・ドイツの元看守死去,
+5,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/economy/electric_household_appliances/,スマートTV 普及の鍵は提案力,
+6,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/sports/shinji_kagawa/,香川が先制ゴール 今季9点目,
+7,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/sports/yakult_swallows/,燕・バレ 金髪で2軍降格危機,
+8,http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/entertainment/illness_and_injury/,くりぃむ上田が声帯手術へ,

test/app/sample/fixtures/feeds.yahoo.json

+[
+  {
+    "pk": 1, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/world/taiwan/", 
+      "title": "\u5e73\u6cbc\u6c0f\u3001\u53f0\u6e7e\u306b\u732e\u82b1\u554f\u984c\u8b1d\u7f6a"
+    }
+  }, 
+  {
+    "pk": 2, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/domestic/solitary_death/", 
+      "title": "\u6bcd\u5b50\u6b7b\u4ea1 \u590f\u304b\u3089\u606f\u5b50\u901a\u6240\u305b\u305a"
+    }
+  }, 
+  {
+    "pk": 3, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/economy/aij/", 
+      "title": "AIJ\u793e\u9577 \u5b9f\u614b\u3068\u9006\u306e\u300c\u54f2\u5b66\u300d"
+    }
+  }, 
+  {
+    "pk": 4, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/world/germany/", 
+      "title": "\u30ca\u30c1\u30b9\u30fb\u30c9\u30a4\u30c4\u306e\u5143\u770b\u5b88\u6b7b\u53bb"
+    }
+  }, 
+  {
+    "pk": 5, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/economy/electric_household_appliances/", 
+      "title": "\u30b9\u30de\u30fc\u30c8TV \u666e\u53ca\u306e\u9375\u306f\u63d0\u6848\u529b"
+    }
+  }, 
+  {
+    "pk": 6, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/sports/shinji_kagawa/", 
+      "title": "\u9999\u5ddd\u304c\u5148\u5236\u30b4\u30fc\u30eb \u4eca\u5b639\u70b9\u76ee"
+    }
+  }, 
+  {
+    "pk": 7, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/sports/yakult_swallows/", 
+      "title": "\u71d5\u30fb\u30d0\u30ec \u91d1\u9aea\u30672\u8ecd\u964d\u683c\u5371\u6a5f"
+    }
+  }, 
+  {
+    "pk": 8, 
+    "model": "sample.feed", 
+    "fields": {
+      "type": "", 
+      "tags": [], 
+      "link": "http://rd.yahoo.co.jp/rss/l/topics/topics/*http://dailynews.yahoo.co.jp/fc/entertainment/illness_and_injury/", 
+      "title": "\u304f\u308a\u3043\u3080\u4e0a\u7530\u304c\u58f0\u5e2f\u624b\u8853\u3078"
+    }
+  }
+]

test/app/sample/models.py

 # -*- coding: utf-8 -*-
 from django.db import models
+from django.contrib.auth.models import User
+from csvutils.models import CsvModelManager
 
-# Create your models here.
+class Tag(models.Model):
+    name=models.CharField(u'Tag Name',max_length=255 , )
+    users = models.ManyToManyField(User,verbose_name=u'Tagged User')
+
+class FeedManager(models.Manager,CsvModelManager):
+    pass
 
 class Feed(models.Model ):
     link = models.CharField(u'Link',max_length=255 , )
     title = models.CharField(u'Title',max_length=400,)
-    type  = models.CharField(u'Type',max_length=30, )
+    type  = models.CharField(u'Type',max_length=30, default="")
+    tags = models.ManyToManyField(Tag,verbose_name="Tags",null=True,blank=True,default=None )
+    
+    objects =FeedManager()
 
+    def __unicode__(self):
+        return self.title
+
+

test/app/sample/tests.py

 """
 
 from django.test import TestCase
-import os
+import os,sys
+from django.core import serializers
+
 
 class UtilsTest(TestCase):
+
+    script = __file__
+
+    def fixture_path(self,path,filename):
+        return os.path.join(
+                os.path.join( os.path.dirname(os.path.abspath(self.script)), path ),
+                filename)
+
+    def open_fixture(self,path,filename):
+        return open( self.fixture_path(path,filename)  
+        )   
+        
+    def load_fixture(self,path,filename):
+        map(lambda m:m.save(),  
+            serializers.deserialize('json',self.open_fixture(path,filename))
+        )
+
     def test_simple(self):
         """
         python manage.py test sample.UtilsTest.test_simple
         import feedparser
         from models import Feed
         d = feedparser.parse('http://rss.dailynews.yahoo.co.jp/fc/rss.xml') 
+        print dir(d)
+        print d['headers']
         for e in d['entries']:
             init = dict( [(k,v) for k,v in e.items() if k in ['link','title',]] )
+            print init
             for k,v in init.items():
                 print k,v
             Feed(**init).save()
 
-        print Feed.objects.all()
+        serializers.serialize('json',   
+            Feed.objects.all(), 
+            ensure_ascii=True,indent=2,
+            stream=open('feeds.yahoo.json','w'))
+        
+#        print Feed.objects.all()
 
+    def test_model_to_csv(self):
+        """
+        python manage.py test sample.UtilsTest.test_model_to_csv
+
+        """
+        fp="fixtures"
+        jf="feeds.yahoo.json"
+        self.load_fixture(fp,jf)
+
+        from models import Feed
+        count=Feed.objects.count()
+
+        from csvutils.utils import UnicodeWriter 
+        
+        wr = UnicodeWriter(open(self.fixture_path('fixtures','dump.csv'),'w'))
+        for i, row in Feed.objects.csv_dump(None):
+            wr.writerow(row)
+        self.assertEqual(i,count)
+        
+    def test_csv_to_model(self):
+        """
+        python manage.py test sample.UtilsTest.test_csv_to_model
+
+        """
+        from models import Feed
+        Feed.objects.all().delete()
+        self.assertEqual(0,Feed.objects.count() )
+        for i, feed, e  in Feed.objects.csv_load(open(self.fixture_path('fixtures','dump.csv'))):
+            print i,feed,e
+        self.assertEqual(i+1,Feed.objects.count() )

test/app/settings.py

         'PASSWORD': 'csvutils',                  # Not used with sqlite3.
         'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
         'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+        'TEST_CHARSET': 'utf8',
+        'TEST_DATABASE_COLLATION': 'utf8_general_ci',
     }
 }
 
     # 'django.contrib.admindocs',
     'csvutils', 
     #
-    'mandb', 
     'sample',
 )
 
         },
     }
 }
+
+# ---
+INSTALLED_APPS += ( 'mandb',  )
+
+# --- logger
+import applogs
+applogs.config(LOGGING)
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.