cedrik
cedrik

Reputation: 559

How to clone/duplicate an entry, using class-based views, with model inheritance?

So... I try to explain : I have a "generic" model (who should certainly be abstract) GenericProduct and his child model Product. This gives me 2 tables with some entries of course. I want to be able to duplicate an entry, via templates. To do that (Class-based view), I created a class Duplicate(CreateView, UpdateView) adn I've overwritten the method get_object to set the "pk" to "None". It works, but the pk I get is the pk of the second table... So the "duplication" doesn't work... I can give you some code to test.

But am I doing it the right way ? In my Generic model I've also created a method

def duplicate(self):
    self.save(force_insert=True)

How could I use it ?

Thanks

Edit : adding code

models.py

from django.db import models

class GenericProduct(models.Model):
    name = models.CharField(max_length=100, unique=True)
    name = models.CharField(max_length=100)

    def duplicate(self): # can it help ?
        self.save(force_insert=True)


class Product(GenericProduct):
    pass

form.py

from django.forms.models import ModelForm

class ProductForm(ModelForm):
    class Meta:
        model = Product
        fields = '__all__'

views.py (~CRUD)

from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, UpdateView
from django.core.urlresolvers import reverse_lazy


class List(ListView):
    model = Product
    context_object_name = "products"
    template_name = "products.html"


class Update(UpdateView):
    model = Product
    form_class = ProductForm
    template_name = "product.html"
    success_url = reverse_lazy('products')

    def get_object(self):
        reference = self.kwargs['reference']
        p = self.model.objects.get(reference=reference)
        p.pk = None
        return p


class Duplicate(CreateView, UpdateView):
    model = Product
    template_name = "product.html"
    success_url = reverse_lazy('products')
    form_class = ProductForm

    def get_object(self):
        reference = self.kwargs['reference']
        p = self.model.objects.get(reference=reference)
        p.genericproduct_ptr_id = None
        return p

products.html (to get the list)

<html>
<body>
<style>
    table {
        border: 1px solid black;
        border-collapse: collapse;
    }
    td, th {
        border: 1px solid black;
        padding: 3px 7px;
    }
</style>
<table>
    <tr>
        <th>actions</th>
        <th>reference</th>
        <th>name</th>
    </tr>
{% for product in products %}
    <tr>
        <td>
            <a href="{% url 'duplicate' product.reference %}">duplicate</a>
        </td>
        <td>
            <a href="{% url 'update' product.reference %}">
                {{ product.reference }}
            </a>
        </td>
        <td>{{ product.name }}</td>
    </tr>
{% empty %}
    <tr>
        <td>no entry</td>
    </tr>
{% endfor %}
</table>
</body>
</html>

product.html (to edit/duplicate a product)

<html>
<body>
    <form action="" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="OK" />
    </form>
    <p>
        <a href="{% url 'products' %}">
            back to list of products
        </a>
    </p>
</body>
</html>

Upvotes: 1

Views: 970

Answers (1)

cedrik
cedrik

Reputation: 559

I make my comment as answer as I am satisfied of it :-) !!

So in models.py, no need of def duplicate(self): self.save(force_insert=True) In views.py, no need of the Duplicate class. I add too the urls.py :

from django.conf.urls import url

urlpatterns = [
    url(r'^products/?$', List.as_view(), name='products'),
    url(r'^create/?$', Create.as_view(), name='create'),
    url(r'^edit/(?P<reference>.*)/?$', Update.as_view(), name='update'),
    url(r'^duplicate/(?P<reference>.*)/?$', Update.as_view(), name='duplicate'),
]

And where it become possible, in product.html :

<html>
<body>
    {% if "duplicate" in request.path|cut:"/" %}{% url "create" as URL %}{% endif %}
    <form action='{{ URL }}' method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="OK" />
    </form>
    <p>
        <a href="{% url 'products' %}">
            back to list of products
        </a>
    </p>
</body>
</html>

The trick is to parse the URL to know if we display the form as a creation or as a duplication. We also could pass an argument in the url configuration, but we already have the information in the URL, so... And then, the form is sent to the action attribute value : "" (empty) if update, creation URL if duplication. That's all.

Regards,

Upvotes: 1

Related Questions