Ticket #645: class-module.patch
File class-module.patch, 27.2 KB (added by , 19 years ago) |
---|
-
django/contrib/comments/models/comments.py
==== Patch <class-module> level 1 Source: d410f328-f502-0410-afc1-bf1322476e67:/django/patches/class-module:2921 Target: d410f328-f502-0410-afc1-bf1322476e67:/django/trunk:2920 Log: r2906@moria: sune | 2005-10-18 21:58:25 +0200 Create patch-branch for "class MODULE" as discussed in http://tinyurl.com/bv7fy (googlegroups). r2921@moria: sune | 2005-10-18 23:33:19 +0200 Implement "class MODEL"; removing support for _module_ and module_constants. === django/contrib/comments/models/comments.py ==================================================================
27 27 site = meta.ForeignKey(core.Site) 28 28 class META: 29 29 db_table = 'comments' 30 module_constants = {31 # min. and max. allowed dimensions for photo resizing (in pixels)32 'MIN_PHOTO_DIMENSION': 5,33 'MAX_PHOTO_DIMENSION': 1000,34 35 # option codes for comment-form hidden fields36 'PHOTOS_REQUIRED': 'pr',37 'PHOTOS_OPTIONAL': 'pa',38 'RATINGS_REQUIRED': 'rr',39 'RATINGS_OPTIONAL': 'ra',40 'IS_PUBLIC': 'ip',41 }42 30 ordering = ('-submit_date',) 43 31 admin = meta.Admin( 44 32 fields = ( … … 111 99 (self.get_user().username, self.submit_date, 112 100 self.comment, self.get_site().domain, self.get_absolute_url()) 113 101 114 def _module_get_security_hash(options, photo_options, rating_options, target): 115 """ 116 Returns the MD5 hash of the given options (a comma-separated string such as 117 'pa,ra') and target (something like 'lcom.eventtimes:5157'). Used to 118 validate that submitted form options have not been tampered-with. 119 """ 120 from django.conf.settings import SECRET_KEY 121 import md5 122 return md5.new(options + photo_options + rating_options + target + SECRET_KEY).hexdigest() 102 class MODULE: 103 # min. and max. allowed dimensions for photo resizing (in pixels) 104 MIN_PHOTO_DIMENSION = 5 105 MAX_PHOTO_DIMENSION = 1000 123 106 124 def _module_get_rating_options(rating_string): 125 """ 126 Given a rating_string, this returns a tuple of (rating_range, options). 127 >>> s = "scale:1-10|First_category|Second_category" 128 >>> get_rating_options(s) 129 ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ['First category', 'Second category']) 130 """ 131 rating_range, options = rating_string.split('|', 1) 132 rating_range = range(int(rating_range[6:].split('-')[0]), int(rating_range[6:].split('-')[1])+1) 133 choices = [c.replace('_', ' ') for c in options.split('|')] 134 return rating_range, choices 107 # option codes for comment-form hidden fields 108 PHOTOS_REQUIRED = 'pr' 109 PHOTOS_OPTIONAL = 'pa' 110 RATINGS_REQUIRED = 'rr' 111 RATINGS_OPTIONAL = 'ra' 112 IS_PUBLIC = 'ip' 135 113 136 def _module_get_list_with_karma(**kwargs):137 """138 Returns a list of Comment objects matching the given lookup terms, with139 _karma_total_good and _karma_total_bad filled.140 """141 kwargs.setdefault('select', {})142 kwargs['select']['_karma_total_good'] = 'SELECT COUNT(*) FROM comments_karma WHERE comments_karma.comment_id=comments.id AND score=1'143 kwargs['select']['_karma_total_bad'] = 'SELECT COUNT(*) FROM comments_karma WHERE comments_karma.comment_id=comments.id AND score=-1'144 return get_list(**kwargs)114 def get_security_hash(options, photo_options, rating_options, target): 115 """ 116 Returns the MD5 hash of the given options (a comma-separated string such as 117 'pa,ra') and target (something like 'lcom.eventtimes:5157'). Used to 118 validate that submitted form options have not been tampered-with. 119 """ 120 from django.conf.settings import SECRET_KEY 121 import md5 122 return md5.new(options + photo_options + rating_options + target + SECRET_KEY).hexdigest() 145 123 146 def _module_user_is_moderator(user): 147 from django.conf.settings import COMMENTS_MODERATORS_GROUP 148 if user.is_superuser: 149 return True 150 for g in user.get_group_list(): 151 if g.id == COMMENTS_MODERATORS_GROUP: 124 def get_rating_options(rating_string): 125 """ 126 Given a rating_string, this returns a tuple of (rating_range, options). 127 >>> s = "scale:1-10|First_category|Second_category" 128 >>> get_rating_options(s) 129 ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ['First category', 'Second category']) 130 """ 131 rating_range, options = rating_string.split('|', 1) 132 rating_range = range(int(rating_range[6:].split('-')[0]), int(rating_range[6:].split('-')[1])+1) 133 choices = [c.replace('_', ' ') for c in options.split('|')] 134 return rating_range, choices 135 136 def get_list_with_karma(**kwargs): 137 """ 138 Returns a list of Comment objects matching the given lookup terms, with 139 _karma_total_good and _karma_total_bad filled. 140 """ 141 kwargs.setdefault('select', {}) 142 kwargs['select']['_karma_total_good'] = 'SELECT COUNT(*) FROM comments_karma WHERE comments_karma.comment_id=comments.id AND score=1' 143 kwargs['select']['_karma_total_bad'] = 'SELECT COUNT(*) FROM comments_karma WHERE comments_karma.comment_id=comments.id AND score=-1' 144 return get_list(**kwargs) 145 146 def user_is_moderator(user): 147 from django.conf.settings import COMMENTS_MODERATORS_GROUP 148 if user.is_superuser: 152 149 return True 153 return False 150 for g in user.get_group_list(): 151 if g.id == COMMENTS_MODERATORS_GROUP: 152 return True 153 return False 154 154 155 155 class FreeComment(meta.Model): 156 156 # A FreeComment is a comment by a non-registered user. … … 206 206 class META: 207 207 module_name = 'karma' 208 208 unique_together = (('user', 'comment'),) 209 module_constants = {210 # what users get if they don't have any karma211 'DEFAULT_KARMA': 5,212 'KARMA_NEEDED_BEFORE_DISPLAYED': 3,213 }214 209 215 210 def __repr__(self): 216 211 return "%d rating by %s" % (self.score, self.get_user()) 217 212 218 def _module_vote(user_id, comment_id, score): 219 try: 220 karma = get_object(comment__id__exact=comment_id, user__id__exact=user_id) 221 except KarmaScoreDoesNotExist: 222 karma = KarmaScore(None, user_id, comment_id, score, datetime.datetime.now()) 223 karma.save() 224 else: 225 karma.score = score 226 karma.scored_date = datetime.datetime.now() 227 karma.save() 213 class MODULE: 214 # what users get if they don't have any karma 215 DEFAULT_KARMA = 5 216 KARMA_NEEDED_BEFORE_DISPLAYED = 3 228 217 229 def _module_get_pretty_score(score): 230 """ 231 Given a score between -1 and 1 (inclusive), returns the same score on a 232 scale between 1 and 10 (inclusive), as an integer. 233 """ 234 if score is None: 235 return DEFAULT_KARMA 236 return int(round((4.5 * score) + 5.5)) 218 def vote(user_id, comment_id, score): 219 try: 220 karma = get_object(comment__id__exact=comment_id, user__id__exact=user_id) 221 except KarmaScoreDoesNotExist: 222 karma = KarmaScore(None, user_id, comment_id, score, datetime.datetime.now()) 223 karma.save() 224 else: 225 karma.score = score 226 karma.scored_date = datetime.datetime.now() 227 karma.save() 237 228 229 def get_pretty_score(score): 230 """ 231 Given a score between -1 and 1 (inclusive), returns the same score on a 232 scale between 1 and 10 (inclusive), as an integer. 233 """ 234 if score is None: 235 return DEFAULT_KARMA 236 return int(round((4.5 * score) + 5.5)) 237 238 238 class UserFlag(meta.Model): 239 239 user = meta.ForeignKey(auth.User) 240 240 comment = meta.ForeignKey(Comment) … … 246 246 def __repr__(self): 247 247 return "Flag by %r" % self.get_user() 248 248 249 def _module_flag(comment, user): 250 """ 251 Flags the given comment by the given user. If the comment has already 252 been flagged by the user, or it was a comment posted by the user, 253 nothing happens. 254 """ 255 if int(comment.user_id) == int(user.id): 256 return # A user can't flag his own comment. Fail silently. 257 try: 258 f = get_object(user__id__exact=user.id, comment__id__exact=comment.id) 259 except UserFlagDoesNotExist: 260 from django.core.mail import mail_managers 261 f = UserFlag(None, user.id, comment.id, None) 262 message = 'This comment was flagged by %s:\n\n%s' % (user.username, comment.get_as_text()) 263 mail_managers('Comment flagged', message, fail_silently=True) 264 f.save() 249 class MODULE: 250 def flag(comment, user): 251 """ 252 Flags the given comment by the given user. If the comment has already 253 been flagged by the user, or it was a comment posted by the user, 254 nothing happens. 255 """ 256 if int(comment.user_id) == int(user.id): 257 return # A user can't flag his own comment. Fail silently. 258 try: 259 f = get_object(user__id__exact=user.id, comment__id__exact=comment.id) 260 except UserFlagDoesNotExist: 261 from django.core.mail import mail_managers 262 f = UserFlag(None, user.id, comment.id, None) 263 message = 'This comment was flagged by %s:\n\n%s' % (user.username, comment.get_as_text()) 264 mail_managers('Comment flagged', message, fail_silently=True) 265 f.save() 265 266 266 267 class ModeratorDeletion(meta.Model): 267 268 user = meta.ForeignKey(auth.User, verbose_name='moderator') -
django/models/core.py
=== django/models/core.py ==================================================================
10 10 def __repr__(self): 11 11 return self.domain 12 12 13 def _module_get_current(): 14 "Returns the current site, according to the SITE_ID constant." 15 from django.conf.settings import SITE_ID 16 return get_object(pk=SITE_ID) 13 class MODULE: 14 def get_current(): 15 "Returns the current site, according to the SITE_ID constant." 16 from django.conf.settings import SITE_ID 17 return get_object(pk=SITE_ID) 17 18 18 19 class Package(meta.Model): 19 20 label = meta.CharField(maxlength=20, primary_key=True) … … 104 105 session_key = meta.CharField(maxlength=40, primary_key=True) 105 106 session_data = meta.TextField() 106 107 expire_date = meta.DateTimeField() 107 class META:108 module_constants = {109 'base64': base64,110 'md5': md5,111 'pickle': pickle,112 'random': random,113 'sys': sys,114 }115 108 116 109 def get_decoded(self): 117 110 from django.conf.settings import SECRET_KEY … … 122 115 raise SuspiciousOperation, "User tampered with session cookie." 123 116 return pickle.loads(pickled) 124 117 125 def _module_encode(session_dict):126 "Returns the given session dictionary pickled and encoded as a string."127 from django.conf.settings import SECRET_KEY128 pickle d = pickle.dumps(session_dict)129 pickled_md5 = md5.new(pickled + SECRET_KEY).hexdigest()130 return base64.encodestring(pickled + pickled_md5)118 class MODULE: 119 base64 = base64 120 md5 = md5 121 pickle = pickle 122 random = random 123 sys = sys 131 124 132 def _module_get_new_session_key(): 133 "Returns session key that isn't being used." 134 from django.conf.settings import SECRET_KEY 135 # The random module is seeded when this Apache child is created. 136 # Use person_id and SECRET_KEY as added salt. 137 while 1: 138 session_key = md5.new(str(random.randint(0, sys.maxint - 1)) + SECRET_KEY).hexdigest() 139 try: 140 get_object(session_key__exact=session_key) 141 except SessionDoesNotExist: 142 break 143 return session_key 125 def encode(session_dict): 126 "Returns the given session dictionary pickled and encoded as a string." 127 from django.conf.settings import SECRET_KEY 128 pickled = pickle.dumps(session_dict) 129 pickled_md5 = md5.new(pickled + SECRET_KEY).hexdigest() 130 return base64.encodestring(pickled + pickled_md5) 144 131 145 def _module_save(session_key, session_dict, expire_date): 146 s = Session(session_key, encode(session_dict), expire_date) 147 if session_dict: 148 s.save() 149 else: 150 s.delete() # Clear sessions with no data. 151 return s 132 def get_new_session_key(): 133 "Returns session key that isn't being used." 134 from django.conf.settings import SECRET_KEY 135 # The random module is seeded when this Apache child is created. 136 # Use person_id and SECRET_KEY as added salt. 137 while 1: 138 session_key = md5.new(str(random.randint(0, sys.maxint - 1)) + SECRET_KEY).hexdigest() 139 try: 140 get_object(session_key__exact=session_key) 141 except SessionDoesNotExist: 142 break 143 return session_key 144 145 def save(session_key, session_dict, expire_date): 146 s = Session(session_key, encode(session_dict), expire_date) 147 if session_dict: 148 s.save() 149 else: 150 s.delete() # Clear sessions with no data. 151 return s -
django/models/auth.py
=== django/models/auth.py ==================================================================
39 39 help_text="In addition to the permissions manually assigned, this user will also get all permissions granted to each group he/she is in.") 40 40 user_permissions = meta.ManyToManyField(Permission, blank=True, filter_interface=meta.HORIZONTAL) 41 41 class META: 42 module_constants = {43 'SESSION_KEY': '_auth_user_id',44 }45 42 ordering = ('username',) 46 43 exceptions = ('SiteProfileNotAvailable',) 47 44 admin = meta.Admin( … … 154 151 raise SiteProfileNotAvailable 155 152 return self._profile_cache 156 153 157 def _module_create_user(username, email, password): 158 "Creates and saves a User with the given username, e-mail and password." 159 import md5 160 password_md5 = md5.new(password).hexdigest() 161 now = datetime.datetime.now() 162 user = User(None, username, '', '', email.strip().lower(), password_md5, False, True, False, now, now) 163 user.save() 164 return user 154 class MODULE: 155 SESSION_KEY = '_auth_user_id' 165 156 166 def _module_make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'): 167 "Generates a random password with the given length and given allowed_chars" 168 # Note that default value of allowed_chars does not have "I" or letters 169 # that look like it -- just to avoid confusion. 170 from random import choice 171 return ''.join([choice(allowed_chars) for i in range(length)]) 157 def create_user(username, email, password): 158 "Creates and saves a User with the given username, e-mail and password." 159 import md5 160 password_md5 = md5.new(password).hexdigest() 161 now = datetime.datetime.now() 162 user = User(None, username, '', '', email.strip().lower(), password_md5, False, True, False, now, now) 163 user.save() 164 return user 172 165 166 def make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'): 167 "Generates a random password with the given length and given allowed_chars" 168 # Note that default value of allowed_chars does not have "I" or letters 169 # that look like it -- just to avoid confusion. 170 from random import choice 171 return ''.join([choice(allowed_chars) for i in range(length)]) 172 173 173 class Message(meta.Model): 174 174 user = meta.ForeignKey(User) 175 175 message = meta.TextField() … … 190 190 verbose_name_plural = 'log entries' 191 191 db_table = 'auth_admin_log' 192 192 ordering = ('-action_time',) 193 module_constants = {194 'ADDITION': 1,195 'CHANGE': 2,196 'DELETION': 3,197 }198 193 199 194 def __repr__(self): 200 195 return str(self.action_time) … … 219 214 """ 220 215 return "%s/%s/%s/" % (self.get_content_type().package, self.get_content_type().python_module_name, self.object_id) 221 216 222 def _module_log_action(user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): 223 e = LogEntry(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message) 224 e.save() 217 class MODULE: 218 ADDITION = 1 219 CHANGE = 2 220 DELETION = 3 221 222 def log_action(user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): 223 e = LogEntry(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message) 224 e.save() -
django/core/meta/__init__.py
=== django/core/meta/__init__.py ==================================================================
151 151 fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False, 152 152 where_constraints=None, object_name=None, app_label=None, 153 153 exceptions=None, permissions=None, get_latest_by=None, 154 order_with_respect_to=None , module_constants=None):154 order_with_respect_to=None): 155 155 156 156 # Save the original function args, for use by copy(). Note that we're 157 157 # NOT using copy.deepcopy(), because that would create a new copy of … … 194 194 self.ordering = ('_order',) 195 195 else: 196 196 self.order_with_respect_to = None 197 self.module_constants = module_constants or {}198 197 self.admin = admin 199 198 200 199 # Calculate one_to_one_field. … … 463 462 permissions = meta_attrs.pop('permissions', None), 464 463 get_latest_by = meta_attrs.pop('get_latest_by', None), 465 464 order_with_respect_to = meta_attrs.pop('order_with_respect_to', None), 466 module_constants = meta_attrs.pop('module_constants', None),467 465 ) 468 466 469 467 if meta_attrs != {}: … … 476 474 else: 477 475 new_mod = types.ModuleType(opts.module_name) 478 476 477 # Update new module with attributes from 'class MODULE' 478 try: 479 module_attrs = attrs.pop('MODULE').__dict__ 480 del module_attrs['__module__'] 481 del module_attrs['__doc__'] 482 except KeyError: 483 module_attrs = {} 484 new_mod.__dict__.update(module_attrs) 485 486 # Extract functions into custom_functions for later massage 487 custom_functions = {} 488 for k, v in new_mod.__dict__.items(): 489 if hasattr(v, 'custom') or isinstance(v, types.FunctionType): 490 del new_mod.__dict__[k] 491 custom_functions[k] = v 492 479 493 # Collect any/all custom class methods and module functions, and move 480 494 # them to a temporary holding variable. We'll deal with them later. 481 495 if replaces_module is not None: 482 # Initialize these values to the base class' custom_methods and 483 # custom_functions. 496 # Initialize these values to the base class' custom_methods. 484 497 custom_methods = dict([(k, v) for k, v in new_mod.Klass.__dict__.items() if hasattr(v, 'custom')]) 485 custom_functions = dict([(k, v) for k, v in new_mod.__dict__.items() if hasattr(v, 'custom')])486 498 else: 487 custom_methods , custom_functions = {},{}499 custom_methods = {} 488 500 manipulator_methods = {} 489 501 for k, v in attrs.items(): 490 502 if k in ('__module__', '__init__', '_overrides', '__doc__'): … … 494 506 # it's a custom function/method. 495 507 v.custom = True 496 508 if k.startswith(MODEL_FUNCTIONS_PREFIX): 497 custom_functions[k[len(MODEL_FUNCTIONS_PREFIX):]] = v498 elif k.startswith(MANIPULATOR_FUNCTIONS_PREFIX):509 raise AssertionError, '%s prefix deprecated' % MODEL_FUNCTIONS_PREFIX 510 if k.startswith(MANIPULATOR_FUNCTIONS_PREFIX): 499 511 manipulator_methods[k[len(MANIPULATOR_FUNCTIONS_PREFIX):]] = v 500 512 else: 501 513 custom_methods[k] = v … … 515 527 exc.__module__ = MODEL_PREFIX + '.' + opts.module_name # Set this explicitly, as above. 516 528 setattr(new_mod, exception_name, exc) 517 529 518 # Create any module-level constants, if applicable.519 for k, v in opts.module_constants.items():520 setattr(new_mod, k, v)521 522 530 # Create the default class methods. 523 531 attrs['__init__'] = curry(method_init, opts) 524 532 attrs['__eq__'] = curry(method_eq, opts) -
tests/testapp/models/custom_methods.py
=== tests/testapp/models/custom_methods.py ==================================================================
50 50 # positional arguments to Article(). 51 51 return [Article(*row) for row in cursor.fetchall()] 52 52 53 class MODULE: 54 answer = 42 55 56 def the_answer(): 57 return answer 58 59 def the_question(): 60 return the_answer() / 7 61 53 62 API_TESTS = """ 54 63 # Create a couple of Articles. 55 64 >>> from datetime import date … … 69 78 [Area man programs in Python] 70 79 >>> b.get_articles_from_same_day_2() 71 80 [Area man programs in Python] 81 82 >>> articles.the_answer() 83 42 84 >>> articles.the_question() 85 6 72 86 """