Question

I have written a simple feedback application in django. It's not particulairly complex, basically it allows authenticated users to write a shot message with a subject line and submit that message via a form. I then allows who are in a selected group to view user submitted feedback. In the future I may add more functionality but for now it does what I want.

Here comes my question, the site I'm building has multiple places where I would like to use the feedback app, for example I have a "what do you think of the site?" kind of page at /dev/feedback/ I also have one for customer support feedback at "/support/feedback/" Currently I have just copied the code from my mysite.apps.dev.feedback over to mysite.apps.support.feedback.

The problem is that this has now created two separate copies of the same code. Despite having just written the app the two versions are already starting to diverge which is annoying. My question is simply how do I create multiple instances of the same app in a django site with distinct database models?

Some resources I've found related but not helpful are https://docs.djangoproject.com/en/dev/topics/http/urls/ and Reversing namespaced URLs in Django: multiple instances of the same app The first page does not offer much on the issue and the second page provides somewhat cludgey and impractical solutions that seem to be both unrelated and more work than their worth. Is there a proper way to implement multiple instances of the same django app?

Was it helpful?

Solution

Single model approach

I'd personally try to keep this as one app and have a view that can handle being posted from multiple locations / tag them appropriately.

As S.Lott says, this is the way to go. I am providing alternatives if you're curious about methods to keep your code in one place in other situations.

For example, you could add a category field to your model, set up a single url conf which accepts an argument in the URL such as /(?P<category>\w+/feedback/$ and have the view simply tag the feedback with the appropriate category.

class MyForm(forms.ModelForm):
    class Meta:
        model = Feedback

def my_view(request, category):
    form = MyForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            feedback = form.save(commit=False)
            feedback.category = category
            feedback.save()
            return http.HttpResponse("Thanks for posting!")
    return render(request, "mytemplate.html", {'form': form})

# urls.py
(r'^(?P<category>\w+)/feedback/$', 'my_view')

# user can visit dev/feedback or support/feedback and the feedback will be tagged appropriately

Abstract base class

Another solution is to build an abstract base class, then create subclasses for your distinct tables. That should solve the issue with your code getting out of sync.

You'd have a single abstract model (which has no tables) from which your "real" models in your separate apps would be based on.

Dynamically generated views

If you must have separate models, you could potentially write a dynamically constructed view.

def view_generator(model_class):
    class MyForm(forms.ModelForm):
         class Meta:
              model = model_class

    def my_view(request):
        form = MyForm(request.POST or None)
        if request.method == 'POST':
            if form.is_valid():
                form.save()
                return http.HttpResponse("Thanks for posting!")
        return render(request, "mytemplate.html", {'form': form})
    return my_view


# urls.py
from foo import view_generator

(r'^my_first_feedback_form', view_generator(Model1))
(r'^my_second_feedback_form', view_generator(Model2l))

OTHER TIPS

how do I create multiple instances of the same app in a django site with distinct database models?

You shouldn't.

You simply use the feedback app model in the other two apps with a simple from feedback.models import Feedback.

Then your support app can create, retrieve, update and delete Feedback objects.

Your dev app, similarly, can create, retrieve, update and delete Feedback objects because it imported the model.

That's all that's required: import.

Thanks Yuji Tomita for a very thorough answer, my final solution is derived very closely from his suggestion, but is different enough that I thought I would post it as another option if someone else runs into the same situation that I am in.

Firstly in my mysite.apps.feedback.models file I put

class Feedback( models.Model ):
   subject = models.TextField( max_length=100 )
   body = models.TextField( max_length=100 )
   # Some other stuff here...
   # Finally I used the suggestion above and created a field which I 
   # use to label each entry as belonging to a specific instance of the app.
   instance_name = models.TextField( max_length=20 )

In my mysite.apps.feedback.views file I put

def save_message( request, instance_name ):
    if request.method == 'POST':
        form = FeedbackFrom( request.POST )
        if form.is_valid():
            form.instance.instance_name = instance_name
            form.save()
            return render("feedback/thanks.html")
         else:
             return render("feedback/submit.html", {'form':form })
     else:
         return render("feedback/submit.html",{'form':FeedbackForm()})

@user_passes_test( is_staff )
def all_messages( request, instance_name ):
    messages = Feedback.objects.filter( instance_name = instance_name )
    return render("feedback/view_all.html",{'feedback':messages} )

In my mysite.apps.dev.urls file I put

url(r'^feedback/', include('mysite.apps.feedback.urls'),
    {'instance_name':'dev'}),

In my mysite.apps.support.urls file I put

url(r'^feedback/', include('mysite.apps.feedback.urls'),
    {'instance_name':'support'}),

This will separate feedback messages by app instance. Note that my actual code is more complex but this should be good enough for anyone with a similar problem to get a solution up and running pretty quickly. Hope this is useful to anyone in a similar situation. Thanks again to Yuji Tomita for the suggestions upon which this solution is based.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top