DetailView with data from two other Models - python

I would like to get some advice from the community.
I have recenlty started learning Django and have a question regarding the structure of the application.
I have a URL http://127.0.0.1:8000/asset/2/, a DetailView for my Asset model which also has two card blocks that houses data for two other models Tenant and Service. Check the screenshot below.
I am generating the above view from the asset/views.py file. Code as below.
class AssetMultipleDetailView(LoginRequiredMixin, UserPassesTestMixin, DetailView):
model = Asset
context_object_name = 'asset'
template_name = 'asset/asset_multiple_detail.html'
def test_func(self):
asset_multiple = self.get_object()
if self.request.user == asset_multiple.owner:
return True
return False
def get_context_data(self, **kwargs):
context = super(AssetMultipleDetailView, self).get_context_data(**kwargs)
context['tenants'] = Tenant.objects.filter(asset=context['asset']).order_by('created')
context['services'] = Service.objects.filter(asset=context['asset']).order_by('created')
return context
When you click on the Add New Tenant button, I use the below URL in tenant/urls.py
path('new/asset/<int:pk>/', TenantAssetCreateView.as_view(), name='tenant_asset_create'),
This URL generates a CreateView for Tenant. I use the primary key of the asset in the URL to load up only the right asset to the Asset selection field. Please see the image below.
Everything works well.
I would like to know whether is this the best way to achieve this? Will this be easily maintainable as there are more views similar to this upcoming in the application.
Any advice is much appreciated. Thank you in advance.

I am not quite sure what your models look like. Does tenant have a manytomany relation to asset (a tenant can be related to any amount of assets)? Or does tenant have a foreign key to asset in your design (a tenant has exactly one related asset)? Based on the screenshot I assume the latter.
Or do you want an asset to only have one tenant (foreign key on asset to tenant)?
Loading the correct asset from the URL is perfectly valid. You should maybe make asset in the form disabled, so it can not be manipulated.
In the CreateView you could override form_valid(self,form) to set self.object.asset to the one you need.

Related

How to redirect from a CreateView of model A to another CreateView of a model B while passing key information from model A to the next view?

I am new to Django and do not know what I need to know to do this task. I am tasked with creating a web app that has two models. Model A is the employee and model B is a company that contains many employees. During signup, I have a form for model A. Once model A form is filled out, I need to pass an id from the employee to the company signup url so that when I save the company model to the table, I can make sure that the employee id is stored and so the two tables are related. How do I go about sending the employee_id to the company form page? Do I need to use some sort of redirect?
Flow: dashboard/employee_signup -> dashboard/company_signup -> completed_signup
I've looked through multiple tutorials on Django and most seem to be too simple to solve what I need done.
Here is my EmployeeSignUpView. Right now it redirects to a 'login' page. I need to instead redirect to a CompanySignUpView while passing along an employee_id. A company can't have zero employees, so the first person to signup for the company needs to be stored in the company model. The company table includes a column that stores a list of employees in that company. So a OneToMany relationship.
class EmployeeSignUpView(CreateView):
form_class = FSPEmployeeCreationForm
success_url = reverse_lazy('login')
template_name = 'employee_signup.html'
You redirect to something like
reverse('b:create_b', kwargs={"pk":a.pk}
The URL (in app 'b') is something like
url(r'create/(?P<pk>\d+)/$', b_create.as_view(), name='create_b'),
And you can pick up this parsed pk and convrt it into a full object by subclassing the dispatch method in b_create:
def dispatch( self, request, *a, **kw):
# resolve kwarg to self.a_object
self.a_object = get_object_or_404( A_model, pk = self.kwargs.get('pk','?') )
return super().dispatch( request, *a, **kw)
Doing this early means that the rest of your view code can everywhere reference self.a_object. It might be more efficient to do this only in post or form_valid, if it's not needed to (say) generate default values for the initial get of the form, or not needed until all the posted data is valid.
Instead of providing a static success_url, you can define get_success_url to return a URL that depends on the created object.
Assuming your CompanySignUpView has a URL as follows:
path('company_sign_up/<int:employee_id>/', views.CompanySignUpView.as_view(), name='company_sign_up')
then you would do:
class EmployeeSignUpView(CreateView):
form_class = FSPEmployeeCreationForm
template_name = 'employee_signup.html'
def get_success_url(self):
return reverse('company_sign_up', kwargs={'employee_id': self.object.id})
Edit
In your CompanySignUpView you can get the employee ID in the form_valid method:
class CompanySignUpView(CreateView):
...
def form_valid(self, form):
form.instance.initial_employee_id = self.kwargs['employee_id'] # or whatever the field name is
return super().form_valid(form)

How do I separate user accounts in Django ?

I am using Django to create an app that allows recording of medical information however I am having problems with seperating the user accounts so currently all users see the same information entered. Anyone familiar with django knows how to set the proper permissions and roles and is willing to help a newby out?
I want the user to only access to the account the user creates and the records that the user create.
This is my github link
If you are able to to help I would really appreciate it.
If you want to list only the user's records in your /home . You only need to change the query in your home/views.py, from Identity_unique.objects.all() to Identity_unique.objects.filter(user=request.user)
class Identity_view(TemplateView):
def get(self, request):
form = Identity_form()
Identities = Identity_unique.objects.filter(user=request.user)
var = {'form': form, 'Identities': Identities}
return render(request, self.template_name, var)
Or if you want to filter objects in your Django Admin panel you should read this:
Django Documentation: ModelAdmin.get_queryset(request)
Create a custom user model with an extra field user_type.
https://github.com/samimsk/debatehub/blob/master/devdebatehub/UserApp/models.py
Implemented here.

Django Rest Framework return True if relation exist

I have two questions. How i can just return true if relation exist? For example I have post model and comment model also, comment have foreginKey to post. Now after post serialization i wanna have something like this
{
id: 2
content: "My first post!"
has-comments: True
}
And my second question is how rename field name in model relation? Again we have post and comment. In comment model i have foregin key to post like
post = models.ForeignKey(Post)
Now when i add new comment i send JSON data with {post: postIdHere}. Is possible to change post to postId only in drf not in django model?
I hope you understand me :)
Best Redgards,
Sierran.
The closest thing I can come up with is a custom has_comments field (rather than has-comments) with this in the serializer:
from rest_framework import serializers
class YourSerializer(Either Serializer or ModelSerializer...):
has_comments = serializers.SerializerMethodField()
#staticmethod
def get_has_comments(instance):
# Choose whichever one works for you.
# You did not specify some model names, so I am just making stuff up.
return instance.post_set.exists()
return Comment.objects.filter(post_id=instance.id).exists()
You may also have to specify the field in the serializer's Meta class. When first run, the framework will tell you exactly how.

Django: Initialize model add view with data through admin action

I'm running a photography app in Django. I have a Photograph model and a PhotoSet model with a ManyToManyField relationship to Photograph. I would like to create an admin action where I can select several Photograph objects in the admin list view, choose the "Create photo set from selected photos" action, and be taken to the admin:photography_photoset_add view with the photos field pre-populated with the photos I selected on the previous page. I'd then be able to enter the title, slug, and description as needed. Is this flow possible? I haven't been able to find it after quite a bit of searching and the only route I currently know of would be handling all of this myself with custom add views and storage of my selection in session state. Seems messy.
See this part of the docs:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/#actions-that-provide-intermediate-pages
And this answer:
https://stackoverflow.com/a/4924985/202168
from django.core.urlresolvers import reverse
def create_photoset_from_photos(modeladmin, request, queryset):
ids = ','.join([str(photo_id) for photo_id in queryset.values_list('id', flat=True)])
add_photoset_url = '{url}?photos={ids}'.format(url=reverse('admin:photography_photoset_add'), ids=ids)
return HttpResponseRedirect(add_photoset_url)

Multiple Instances of a django app, does django support this

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?
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))
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.

Categories