Clone this instance and return the new, cloned instance. ``overrides`` should be a dictionary of override values for fields on the cloned instance. M2M or reverse FK relations listed in ``cascade`` iterable will be cascade-cloned. By default, if not listed in
(self, cascade=None, overrides=None, user=None)
| 181 | return super(CDBaseModel, self).save(*args, **kwargs) |
| 182 | |
| 183 | def clone(self, cascade=None, overrides=None, user=None): |
| 184 | """ |
| 185 | Clone this instance and return the new, cloned instance. |
| 186 | ``overrides`` should be a dictionary of override values for fields on |
| 187 | the cloned instance. |
| 188 | M2M or reverse FK relations listed in ``cascade`` iterable will be |
| 189 | cascade-cloned. By default, if not listed in ``cascade``, m2m/reverse |
| 190 | FKs will effectively be cleared (as the remote object will still be |
| 191 | pointing to the original instance, not the cloned one.) |
| 192 | If ``cascade`` is a dictionary, keys are m2m/reverse-FK accessor names, |
| 193 | and values are a callable that takes the queryset of all related |
| 194 | objects and returns those that should be cloned. |
| 195 | """ |
| 196 | if cascade is None: |
| 197 | cascade = {} |
| 198 | else: |
| 199 | try: |
| 200 | cascade.iteritems |
| 201 | except AttributeError: |
| 202 | cascade = dict((i, lambda qs: qs) for i in cascade) |
| 203 | |
| 204 | if overrides is None: |
| 205 | overrides = {} |
| 206 | |
| 207 | overrides["created_time"] = utcnow() |
| 208 | overrides["creator"] = user |
| 209 | overrides["modifier"] = user |
| 210 | |
| 211 | clone = self.__class__() |
| 212 | |
| 213 | for field in self._meta.fields: |
| 214 | if field.primary_key: |
| 215 | continue |
| 216 | val = overrides.get(field.name, getattr(self, field.name)) |
| 217 | setattr(clone, field.name, val) |
| 218 | |
| 219 | clone.save(force_insert=True) |
| 220 | |
| 221 | for name, filter_func in cascade.items(): |
| 222 | mgr = getattr(self, name) |
| 223 | if mgr.__class__.__name__ == "ManyRelatedManager": # M2M |
| 224 | clone_mgr = getattr(clone, name) |
| 225 | existing = set(clone_mgr.all()) |
| 226 | new = set(filter_func(mgr.all())) |
| 227 | clone_mgr.add(*new.difference(existing)) |
| 228 | clone_mgr.remove(*existing.difference(new)) |
| 229 | elif mgr.__class__.__name__ == "RelatedManager": # reverse FK |
| 230 | reverse_name = getattr(self.__class__, name).related.field.name |
| 231 | for obj in filter_func(mgr.all()): |
| 232 | obj.clone(overrides={reverse_name: clone}) |
| 233 | else: |
| 234 | raise ValueError( |
| 235 | "Cannot cascade-clone '{0}'; " |
| 236 | "not a many-to-many or reverse foreignkey.".format(name)) |
| 237 | |
| 238 | return clone |
| 239 | |
| 240 | def delete(self, user=None, permanent=False): |