Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

safe-deposit-box

  • Loading branch information...
commit a06c97c3fe29d2d57f5960d7f37229d3ff9b9a24 0 parents
@gavinwahl authored
1  .gitignore
@@ -0,0 +1 @@
+*.pyc
0  __init__.py
No changes.
14 manage.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+import imp
+try:
+ imp.find_module('settings') # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
+ sys.exit(1)
+
+import settings
+
+if __name__ == "__main__":
+ execute_manager(settings)
0  passwords/__init__.py
No changes.
148 passwords/models.py
@@ -0,0 +1,148 @@
+import couchdb, couchdb.design
+
+server = couchdb.Server()
+try:
+ db = server.create('passwords')
+except couchdb.PreconditionFailed:
+ db = server['passwords']
+
+
+users_by_name = couchdb.design.ViewDefinition('users', 'users_by_name', """
+function(doc) {
+ if ( doc.type == 'user' )
+ emit([doc._id, 0]);
+ if ( doc.type == 'password' )
+ emit([doc.user, 1]);
+}""")
+
+users_by_name.sync(db)
+
+class ValidationError(Exception):
+ pass
+
+class ObjectDoesNotExist(Exception):
+ pass
+
+
+class CouchManager(object):
+ def __init__(self):
+ self.model = None
+
+ def get_by_id(self, id):
+ try:
+ doc = db[id]
+ except couchdb.ResourceNotFound as e:
+ if e.args == (('not_found', 'missing'),):
+ raise self.model.DoesNotExist()
+ else:
+ raise
+ obj = self.model()
+ for field in doc:
+ setattr(obj, field, doc[field])
+ return obj
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ # I think mutating self here will cause problems with model inheritance
+ self.model = type
+ return self
+ else:
+ raise Exception("Can't access manager from instance")
+
+
+class CouchModelMeta(type):
+ def __new__(cls, name, bases, dict):
+ new = type.__new__(cls, name, bases, dict)
+
+ if not hasattr(new, 'objects'):
+ new.objects = CouchManager()
+
+ new.DoesNotExist = type('%s.DoesNotExist' % (name), (ObjectDoesNotExist,), {})
+
+ return new
+
+class CouchModel(object):
+ __metaclass__ = CouchModelMeta
+ def __init__(self, **kwargs):
+ self._id = None
+ self._rev = None
+ self.type = self.__class__.__name__.lower()
+
+ for column in kwargs:
+ setattr(self, column, kwargs[column])
+
+ def save(self):
+ self.validate()
+ (self._id, self._rev) = db.save(self.as_dict())
+
+ def validate(self):
+ pass
+
+ def as_dict(self):
+ data = {}
+ if self._rev:
+ data['_rev'] = self._rev
+ if self._id:
+ data['_id'] = self._id
+ return data
+
+
+
+class UserManager(CouchManager):
+ def with_passwords(self, user_name):
+ """
+ Get a user by name along with all their passwords. It'd be nice to user
+ objects.get_by_id(users_by_name).with_passwords(), like a django
+ queryset, but I'm not sure how to do that yet.
+ """
+ rows = db.view('users/users_by_name', startkey=(user_name, 0), endkey=(user_name, 2), include_docs=True)
+ rows = list(rows)
+ if not rows:
+ raise self.model.DoesNotExist
+ user = User(**(rows[0].doc))
+ user._passwords = [row.doc for row in rows[1:]]
+ return user
+
+class User(CouchModel):
+ objects = UserManager()
+ def as_dict(self):
+ data = super(User, self).as_dict()
+ data.update({
+ '_id': self._id,
+ 'type': 'user'
+ })
+ return data
+
+ def validate(self):
+ if not self._id:
+ raise ValidationError
+
+ @property
+ def name(self):
+ return self._id
+
+ @name.setter
+ def name(self, value):
+ self._id = value
+
+
+ @property
+ def passwords(self):
+ try:
+ return self._passwords
+ except AttributeError:
+ rows = db.view('users/users_by_name', startkey=(self.name, 0), endkey=(self.name, 2), include_docs=True)
+ self._passwords = [row.doc for row in rows]
+ return self._passwords
+
+
+class Password(CouchModel):
+
+ def as_dict(self):
+ data = super(Password, self).as_dict()
+ data.update({
+ 'type': 'password',
+ 'password': self.password,
+ 'user': self.user,
+ })
+ return data
16 passwords/tests.py
@@ -0,0 +1,16 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.assertEqual(1 + 1, 2)
37 passwords/views.py
@@ -0,0 +1,37 @@
+from django.views.generic.base import View
+from django.http import HttpResponse
+import json
+
+from passwords.models import *
+
+class UserView(View):
+ def get(self, request, user_name):
+ user = User.objects.get_by_id(user_name)
+
+ return HttpResponse(json.dumps(user.as_dict()))
+
+ def post(self, request):
+ user = User(name=request.POST['name'])
+ user.save()
+ resp = HttpResponse()
+ resp.status_code = 201
+ resp['Location'] = '/users/%s/' % (user.name)
+ return resp
+
+
+class PasswordView(View):
+ def get(self, request, user_name):
+ user = User.objects.with_passwords(user_name)
+ passwords = user.passwords
+
+ return HttpResponse(json.dumps(passwords))
+
+ def post(self, request, user_name):
+ password = Password()
+ password.password = request.POST['password']
+ password.user = user_name
+ password.save()
+ resp = HttpResponse()
+ resp.status_code = 201
+ resp['Location'] = '/passwords/%s/%s/' % (user_name, password._id)
+ return resp
125 settings.py
@@ -0,0 +1,125 @@
+# Django settings for sdb project.
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+ # ('Your Name', 'your_email@example.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ # couchdb!
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/media/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash.
+# Examples: "http://media.lawrence.com/media/", "http://example.com/media/"
+MEDIA_URL = ''
+
+# Absolute path to the directory static files should be collected to.
+# Don't put anything in this directory yourself; store your static files
+# in apps' "static/" subdirectories and in STATICFILES_DIRS.
+# Example: "/home/media/media.lawrence.com/static/"
+STATIC_ROOT = ''
+
+# URL prefix for static files.
+# Example: "http://media.lawrence.com/static/"
+STATIC_URL = '/static/'
+
+# URL prefix for admin static files -- CSS, JavaScript and images.
+# Make sure to use a trailing slash.
+# Examples: "http://foo.com/static/admin/", "/static/admin/".
+ADMIN_MEDIA_PREFIX = '/static/admin/'
+
+# Additional locations of static files
+STATICFILES_DIRS = (
+ # Put strings here, like "/home/html/static" or "C:/www/django/static".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+# List of finder classes that know how to find static files in
+# various locations.
+STATICFILES_FINDERS = (
+ 'django.contrib.staticfiles.finders.FileSystemFinder',
+ 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
+)
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 't6yth@015=*38zupfjcy-hn^=6@jyknh9z6lzp&&03=&%^h4o8'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+)
+
+ROOT_URLCONF = 'sdb.urls'
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+ 'passwords',
+)
+
+# A sample logging configuration. The only tangible logging
+# performed by this configuration is to send an email to
+# the site admins on every HTTP 500 error.
+# See http://docs.djangoproject.com/en/dev/topics/logging for
+# more details on how to customize your logging configuration.
+LOGGING = {
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'handlers': {
+ 'mail_admins': {
+ 'level': 'ERROR',
+ 'class': 'django.utils.log.AdminEmailHandler'
+ }
+ },
+ 'loggers': {
+ 'django.request': {
+ 'handlers': ['mail_admins'],
+ 'level': 'ERROR',
+ 'propagate': True,
+ },
+ }
+}
12 urls.py
@@ -0,0 +1,12 @@
+from django.conf.urls.defaults import patterns, include, url
+
+from passwords.views import *
+# Uncomment the next two lines to enable the admin:
+# from django.contrib import admin
+# admin.autodiscover()
+
+urlpatterns = patterns('',
+ ('^users/$', UserView.as_view()),
+ ('^users/(?P<user_name>.+)/$', UserView.as_view()),
+ ('^passwords/(?P<user_name>.+)/$', PasswordView.as_view()),
+)
Please sign in to comment.
Something went wrong with that request. Please try again.