def clean(self): """ Detects when the instance is newer than the one used to generate the form. Calls merge() when there is a version conflict, and if there is still anerror, updates the version date in the form to give the user a chance to save anyway. Returns cleaned_data. Raises: ValidationError: When there's a conflict when calling merge(). """ current_version_date = str(self.get_version_date(self.instance)) form_version_date = self.data['version_date'] if current_version_date != form_version_date: ancestor = None if is_versioned(self.instance) and form_version_date: ancestor_model = self.instance.history.as_of(form_version_date) ancestor = model_to_dict(ancestor_model) try: self.cleaned_data = self.merge(self.cleaned_data, self.initial, ancestor) except forms.ValidationError as e: self.data = self.data.copy() self.data['version_date'] = current_version_date raise e return self.cleaned_data
def get_version_date(self, instance): """ Gets the datetime the instance was last modified. This default implementation either uses the field specified by version_date_field or grabs a DateTimeField with auto_now=True. Override this method to customize how you want to determine the version date. """ if hasattr(self, 'version_date_field'): return getattr(instance, self.version_date_field) # if using versioning, return most recent version date if is_versioned(instance): try: return instance.history.most_recent().history_info.date except: return '' # no version_date_field specified, let's try to guess it date_fields = [ field for field in instance._meta.fields if isinstance(field, models.DateTimeField) and field.auto_now ] if date_fields: return getattr(instance, date_fields[0].name) or '' raise ValueError('MergeModelForm does not have a version_date_field')
def clean(self): """ Detects when the instance is newer than the one used to generate the form. Calls merge() when there is a version conflict, and if there is still an error, updates the version date in the form to give the user a chance to save anyway. Returns cleaned_data. Raises: ValidationError: When there's a conflict when calling merge(). """ self.cleaned_data = super(self.__class__.__base__, self).clean() current_version_date = str(self.get_version_date(self.instance)) form_version_date = self.data['version_date'] if current_version_date != form_version_date: ancestor = None if is_versioned(self.instance) and form_version_date: ancestor_model = get_versions(self.instance).as_of( form_version_date) ancestor = model_to_dict(ancestor_model) try: self.cleaned_data = self.merge(self.cleaned_data, self.initial, ancestor) except forms.ValidationError as e: self.data = self.data.copy() self.data['version_date'] = current_version_date raise e else: # Note that we performed a merge, for use elsewhere. self.performed_merge = True return self.cleaned_data
def get_version_date(self, instance): """ Gets the datetime the instance was last modified. This default implementation either uses the field specified by version_date_field or grabs a DateTimeField with auto_now=True. Override this method to customize how you want to determine the version date. """ if hasattr(self, 'version_date_field'): return getattr(instance, self.version_date_field) # if using versioning, return most recent version date if is_versioned(instance): try: return get_versions(instance).most_recent().version_info.date except: return '' # no version_date_field specified, let's try to guess it date_fields = [field for field in instance._meta.fields if isinstance(field, models.DateTimeField) and field.auto_now] if date_fields: return getattr(instance, date_fields[0].name) or '' raise ValueError('MergeMixin cannot find a version_date_field')
def clean(self): """ Detects when the instance is newer than the one used to generate the form. Calls merge() when there is a version conflict, and if there is still anerror, updates the version date in the form to give the user a chance to save anyway. Returns cleaned_data. Raises: ValidationError: When there's a conflict when calling merge(). """ current_version_date = str(self.get_version_date(self.instance)) form_version_date = self.data["version_date"] if current_version_date != form_version_date: ancestor = None if is_versioned(self.instance) and form_version_date: ancestor_model = self.instance.history.as_of(form_version_date) ancestor = model_to_dict(ancestor_model) try: self.cleaned_data = self.merge(self.cleaned_data, self.initial, ancestor) except forms.ValidationError as e: self.data = self.data.copy() self.data["version_date"] = current_version_date raise e return self.cleaned_data
def get_context_data(self, **kwargs): context = super(AddContributorsMixin, self).get_context_data(**kwargs) obj = self.get_object() if not is_versioned(obj): return context users_by_edit_count = obj.versions.exclude( history_user__isnull=True).order_by('history_user').values( 'history_user').annotate( nedits=Count('history_user')).order_by('-nedits') top_3 = users_by_edit_count[:3] num_rest = len(users_by_edit_count[3:]) top_3_html = '' for u_info in top_3: user = User.objects.get(pk=u_info['history_user']) top_3_html += user_link(user, region=self.get_region(), show_username=False, size=24) context['contributors_html'] = top_3_html context['contributors_number'] = num_rest return context
def tearDown(self): """ Remove all created models and all model history. """ for M in self.test_models: for m in M.objects.all(): if is_versioned(M): m.delete(track_changes=False) else: m.delete() self._cleanup_file_environment()
def update_region_for_instance(m, region): if not hasattr(m, 'region'): # Doesn't have an explicit region attribute, so skip. return if is_versioned(m): for m_h in m.versions.all(): m_h.region = region m_h.save() m.region = region m.save(track_changes=False) else: m.save()
def get_context_data(self, **kwargs): context = super(AddContributorsMixin, self).get_context_data(**kwargs) obj = self.get_object() if not is_versioned(obj): return context users_by_edit_count = obj.versions.exclude(history_user__isnull=True).order_by('history_user').values('history_user').annotate(nedits=Count('history_user')).order_by('-nedits') top_3 = users_by_edit_count[:3] num_rest = len(users_by_edit_count[3:]) top_3_html = '' for u_info in top_3: user = User.objects.get(pk=u_info['history_user']) top_3_html += user_link(user, region=self.get_region(), show_username=False, size=24) context['contributors_html'] = top_3_html context['contributors_number'] = num_rest return context
def rename_to(self, pagename): """ Renames the page to `pagename`. Moves related objects around accordingly. """ def _get_slug_lookup(unique_together, obj, new_p): d = {} for field in unique_together: d[field] = getattr(obj, field) d['slug'] = new_p.slug return d def _already_exists(obj): M = obj.__class__ unique_vals = unique_lookup_values_for(obj) if not unique_vals: return False return M.objects.filter(**unique_vals).exists() from redirects.models import Redirect from redirects.exceptions import RedirectToSelf if Page(slug=slugify(pagename), region=self.region).exists(): if slugify(pagename) == self.slug: # The slug is the same but we're changing the name. old_name = self.name self.name = pagename self.save(comment=_('Renamed from "%s"') % old_name) return else: raise exceptions.PageExistsError( _("The page '%s' already exists!") % pagename) # Copy the current page into the new page, zeroing out the # primary key and setting a new name and slug. new_p = copy(self) new_p.pk = None new_p.name = pagename new_p.slug = slugify(pagename) new_p._in_rename = True new_p.save(comment=_('Renamed from "%s"') % self.name) # Get all related objects before the original page is deleted. related_objs = self._get_related_objs() # Cache all ManyToMany values on related objects so we can restore them # later--otherwise they will be lost when page is deleted. for attname, rel_obj_list in related_objs: if not isinstance(rel_obj_list, list): rel_obj_list = [rel_obj_list] for rel_obj in rel_obj_list: rel_obj._m2m_values = dict( (f.attname, list(getattr(rel_obj, f.attname).all())) for f in rel_obj._meta.many_to_many) # Create a redirect from the starting pagename to the new pagename. redirect = Redirect(source=self.slug, destination=new_p, region=self.region) # Creating the redirect causes the starting page to be deleted. redirect.save() # Point each related object to the new page and save the object with a # 'was renamed' comment. for attname, rel_obj in related_objs: if isinstance(rel_obj, list): for obj in rel_obj: obj.pk = None # Reset the primary key before saving. try: getattr(new_p, attname).add(obj) if _already_exists(obj): continue if is_versioned(obj): obj.save(comment=_("Parent page renamed")) else: obj.save() # Restore any m2m fields now that we have a new pk for name, value in obj._m2m_values.items(): setattr(obj, name, value) except RedirectToSelf, s: # We don't want to create a redirect to ourself. # This happens during a rename -> rename-back # cycle. continue else: # This is an easy way to set obj to point to new_p. setattr(new_p, attname, rel_obj) rel_obj.pk = None # Reset the primary key before saving. if _already_exists(rel_obj): continue if is_versioned(rel_obj): rel_obj.save(comment=_("Parent page renamed")) else: rel_obj.save() # Restore any m2m fields now that we have a new pk for name, value in rel_obj._m2m_values.items(): setattr(rel_obj, name, value)