How would you model this in Django? - django

I want to upload a list of companies to my web app and then run some long background scripts on each company (ie. Webscraping). Each upload will be essentially be a batch task.
I suspect I will probably need to queue these batch processes with something like Redis or Celery, though I am not sure because the batch should start immediately after being being submitted.
I am mainly having trouble figuring out how to create my Models for this logic. So any help there would be greatly appreciated. Right now I've set up a Company model and a Batch model. I'm unsure on how to link these models together appropriately so I can sum up the number of employees from each company in a batch.
Please reference these pictures for visual details.
class Company(models.Model):
domain_name = models.CharField(max_length=100, verbose_name="Domain Name")
company_name = models.CharField(max_length=100, verbose_name="Company Name")
num_employees = models.IntegerField(default=0, verbose_name="# of Employees")
batch = models.ForeignKey('Batch', on_delete=models.CASCADE)
def __str__(self):
return f"Company: {self.company_name}\nEmployees: {self.num_employees}"
class Batch(models.Model):
batch_name = models.CharField(max_length=100, verbose_name="Batch Name")
batch_owner = models.CharField(max_length=100, verbose_name="Batch Owner")
create_date = models.DateTimeField('Date Created', auto_now=True)
# processing = models.BooleanField(verbose_name="Processing Status", default=False)
#property
def total_companies(self):
"""Gets imorted companies sum based on batch"""
pass
#property
def total_employees(self):
"""Gets sum of employees based on batch"""
pass

If there are going to be multiple batch instances for a single company, then a foreign key on the batch model to the company model should work.
class Company(models.Model):
some_fields = ....
class Batch(models.Model):
some_fields = ....
company = models.ForeignKey(Company, on_delete=models.PROTECT)
It looks like you already have a field in the Batch image for a company.

Looks like I figured out the models shortly after, I guess I used stackoverflow as my "rubberduck".
So if anyone runs into a similar issue, the model solution is in my question and in the selected answer.
On top of that, I was also able to map out a mock up of how the form view should work. I used some dummy data in a post_dict variable to imitate form data. And of course the request.method == 'GET' Should be 'POST' But for now I want this to process every time I load the respective URL as I haven't completed the form yet.
from itertools import islice
def create_batch_view(request):
if request.method == 'GET':
post_dict = {'batch_name': 'Some batch name',
'batch_owner': 'Developer',
'batch_items': ['company 1', 'company 2', 'company 3'],
}
batch_model = Batch(batch_name=post_dict['batch_name'],
batch_owner=post_dict['batch_owner'])
batch_model.save()
batch_size = 100
objs = (Company(batch=batch_model, company_name='Test %s' % i) for i in post_dict['batch_items'])
while True:
batch = list(islice(objs, batch_size))
if not batch:
break
Company.objects.bulk_create(batch, batch_size)
return redirect('index')
context = {
'form': BatchForm()
}
return render(request, 'createbatchform.html', context)
Hopefully this helps someone else. Now I just need to figure out the simplest way to run scripts on the batches and report back to the view when its done processing. Should I use a queue manager for this? For example. When a new batch is made, all the companies get added to the database like the example above and also a function is added to a queue manager. That function could look something like this:
def batch_process():
# Get batch
# Get Companies related to batch.
# Loop through companies.
# Scrape Data for company and save it back to database.
# Finish and set Batch.processing = False to signal the process is finished.

Related

Using query to match object and then get all objects associated via foreignkey

I am trying to create a job board where a user can search for jobs by zip code. I separated information about the Business into one model and the Job into another. The Business has the address, state, phone number fields etc, while Job has information about the job itself. I designed it this way so the user would not have to reenter information about the physical business every time a job is posted. However now when I try and query the Jobs matching the zip code entered, I really have to grab the Business objects matching that zip code and then grab the Job objects associated with them? Here is my code:
models.py
from django.db import models
from django.contrib.auth.models import User
class Business(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
zip_code = models.CharField(max_length = 10)
class Job(models.Model):
business = models.ForeignKey(Business, on_delete= models.CASCADE)
# ...
views.py:
def search_results(request):
query =request.GET.get('query')
jobs = Job.objects.filter(zipcode = query) # tried this before realizing the `zipcode` field was part of the `Business` model and not the `Job` model.
return render(request, 'job/search_results.html', { 'jobs': jobs})
Thanks for any help. Also, this seems like a question that is probably very common but I was not sure what term to search to find the answer to it, so if this is closed but you can point me in the right direction that would be great!
You want to access the Business through the Job model like so using __:
jobs = Job.objects.filter(business__zip_code=query)
More info: https://docs.djangoproject.com/en/3.0/topics/db/queries/#lookups-that-span-relationships
You can match the related jobs with:
def search_results(request):
query =request.GET.get('query')
jobs = Job.objects.filter(business__zipcode=query)
return render(request, 'job/search_results.html', { 'jobs': jobs})
One can look "through" relations by using double underscores (__).
If you need to show the related business of the Jobs as well, then it might be worth to perform a .select_related(..) on the Business model:
def search_results(request):
query =request.GET.get('query')
jobs = Job.objects.filter(business__zipcode=query).select_related('business')
return render(request, 'job/search_results.html', { 'jobs': jobs})
You can query through Business FK:
Job.objects.filter(business__zip_code=your_code)
Read more here.

Django bulk edit form

In my project I have a many-to-one relation with one Serie having up to 20 samples. I'm trying to implement a system where a user uploads a (csv)file and is then sent to a page where they can fill in any missing data. I haven't found a way to implement this and was looking for some help. The idea is that I want a table with each row having 4 fields, which are linked to the sample in question and the table will have as many rows as there were samples in the (csv)file. Furthermore, if data is already present for one of the fields, I want it to prefill the field in question. In the end, the updated data is committed by pressing a single save button at the bottom so it's supposed to be one big form.
I've added the models below. The attributes which I want to update in the form are index, pool and remarks, which may be present in the csv file already but often they aren't.
Can anyone give me any tips on how to implement this or hint at methods which are best for implementing something like this?
Models
class Sample(models.Model):
name = models.CharField(max_length=20)
pool = models.ForeignKey(Pool, blank=True, null=True)
serie = models.ForeignKey(Serie, blank=True, null = True)
index = models.ForeignKey(Index, blank=True, null=True)
remarks = models.CharField(max_length=50, blank=True)
def __str__(self):
return self.name
class Serie(models.Model):
a_type = models.ForeignKey(Atype)
name = models.IntegerField()
def __str__(self):
return str(self.a_type)+'-'+str(self.name)
Views
def serie_edit(request,serie_id):
try:
serie = Serie.objects.get(pk=serie_id)
except Serie.DoesNotExist:
raise Http404
index_list = Index.objects.all()
sample_list = Sample.objects.filter(serie__id = serie_id)
return render(request, 'samples/serie_edit.html', {'serie':serie, 'sample_list':sample_list, 'index_list':index_list})

Django Select Drop Down menu Flow

What I'm trying to do is a 2 tier search with drop down menus using Select widget, the results will be a listing of the fields from my Meta.model. the first Tier is a a State listing from State.model. Upon a select it is supposed to list out all of the cities with in the selected state, the problem I'm having (and I think its due to my lack of knowledge) is that the city listing is not filtered but a listing of all cities in my database regardless of state. I'm not sure where or how to pass my variable to be able invoke my .filter() statement.
models.py
class Meta(models.Model):
rcabbr = models.CharField(max_length = 15)
slug = models.SlugField(unique=False)
state = models.ForeignKey('State')
rc_state = models.CharField(max_length = 3)
oerp = models.CharField(max_length=18)
subgrp = models.SlugField()
sonus_pic = models.CharField(max_length=8)
ems = models.CharField(max_length=14)
agc = models.CharField(max_length=14)
def __unicode__(self):
return self.rcabbr
class State(models.Model):
name = models.CharField(max_length=2)
slug = models.SlugField(unique=True)
state_long = models.CharField(max_length=15)
owning_site = models.CharField(max_length=12)
def __unicode__(self):
return self.name
return self.state_long
forms.py
class states(forms.Form):
invent = [(k.name,k.state_long) for k in State.objects.all()]
rclist = forms.ChoiceField(widget=forms.Select, choices=invent)
class rateCenter(forms.Form):
invention = [(k.id,k.rcabbr,k.rc_state) for k in Meta.objects.all()]
rcviews = forms.ChoiceField(widget=forms.Select, choices=invention)
views.py
def StateAll(request):
""" This lists out all of the states within the database that
are served"""
rclist = states()
return render(request, 'statelist.html',{'rclist': rclist})
def RcView(request):
""" this should list all the rateCenters within
the state that was selected in StateAll() """
rclist = request.GET['rclist']
forms = rateCenter()
return render(request, 'rclist.html',{'forms': forms})
Logic tells me I should to do my .filter() statement in the forms.py but unsure how to pass the result from the request.GET in StateAll() view. I do have the debug_toolbar installed so I can see the variable u'rclist' and the value u'LA' (my test state). I had this working 100% using hyperlinks however the size of my test database is miniscule in comparison to what is going to be in the production version and HREF's are just not possible.
my understanding is:
ChainedForeignKey(LinkedModel, LinkedModel.field = "field in first Tier", chained_model_field = "current model_field")
so simple model should I think be something like this?
def State(model.models):
name = models.CharField(max_length=20) #this is the state abbreviation
state_long = models.CharFeild(max_length=20)#this is state long form
def Meta(model.models):
state = models.CharField(max_length=20)
slug = models.SlugField(unique = False) #same values as rcabbr
rcabbr = ChainedForeignKey(State, chained_field = "state_long",
chained_model_field = "slug")
.....
Does that look about right........so the First Field in the drop down should be the State_long, once selected the next should be the slug?. at which time the slug should be passed to my urls and the views for the that final page.
I am going to try this however I'm not 100% sure how to do my views and if I need to do something with forms page or does this cover it? The documentation is not user friendly for someone new to this so any input would be most appreciated!
There are many third party libraries django-smart-selects and dajax come to mind - that will automate this for you along with provide you the necessary javascript to filter the form fields on the fly.
If you are investigating those, here is how you would do it with just the django forms:
class RateCenterForm(forms.Form):
rate_center = forms.ModelChoiceField(queryset=Meta.objects.all())
def __init__(self, *args, **kwargs):
state = kwargs.pop('state')
super(RaterCenterForm, self).__init__(*args, **kwargs)
self.fields['rate_center'].queryset = Meta.objects.filter(state=state)
A ModelChoiceField is a select drop down that takes its values from a model.
Now in your view, you would call it like this:
def show_rate_centers(request):
form = RateCenterForm(state='SomeState')
# .. your normal logic here

Django checkout model

What is the best way to create a checkout system with Django that keeps track of the checkout / checkin history?
My models for inventory/models.py
from django.db import models
class Groups(models.Model):
name = models.CharField(max_length=200)
def __unicode__(self):
return self.name
class Inventory(models.Model):
name = models.CharField(max_length=200)
serial = models.CharField(max_length=200)
barcode = models.CharField(max_length=200)
active = models.BooleanField(verbose_name="Active (can be checked out if not out for repair)",blank=True,default=True)
repair = models.BooleanField(verbose_name="Out for repair?",blank=True)
group = models.ForeignKey(Groups)
def __unicode__(self):
return self.name
I am thinking I will need another model that will store the checkout / in information? I am guessing I will need to only obtain the last one so that I know if it is checked in or out? I want to keep a history of the items so that I can create a report with it.
How would I go about making it so I have a history of the items and if the items can be checked in or out?
Yes, it isn't totally clear from your question what a checkout/checkin is, but my guess is you want something like
class Checkout(models.Model)
item = models.ForeignKey(Inventory)
user = models.ForeignKey(User)
checked_out = models.DateTimeField()
checked_in = models.DateTimeField(null=True)
...
You would then create one of these objects each time an item was checked out, and then update it to set the checkin date when it was checked back in.
To find the current checkout (or determine if something is not checked out) you could do a query like:
try:
checkout = Checkout.objects.get(item=my_item, checked_in=None)
except Checkout.DoesNotExist:
#item isn't checked out
checkout = None

Django complex query comparing 2 models

This may be a design question.
Question is "What is the best way to find offers that needs to have feedback sent by logged in user". In Feedbacks site there are 3 tabs: "Sent", "Received", "Send feedback".
"Send feedback" tab there's a table with "Offer id","username(buyer/sender" and "Send feedback" link pointing to feedback form.
Here's the code which should help understand what I mean.
Offers are displayed until some user buys it.
Offer is being closed, and new Order (storing order details) instance is created for this offer.
I'm trying to implement a Feedback app, where both sides of offer transaction can
send feedback about transaction.
Let's skip the "ended" or "running" offer problem.
class Offer(models.Model):
"""Offer is displayed for 5 days, then it's being ended by run everyday cron script.
If someone buys the offer end_time is being set, and offer is treated as ended.
Both sides of transaction may send feedback.
"""
whose = models.ForeignKey(User, verbose_name="User who has created the offer")
end_time = models.DateTimeField(blank=True, null=True, help_text="")
field = ()
fields2 = ()
order = models.ForeignKey(Order, balnk=True, null=True, help_text="Order details")
class Order(models.Model):
"""stores order details like user, date, ip etc."""
order_1_field_details = ()
who = models.ForeignKey(User, verbose_name="User who bought the offer")
offer_id = models.PositiveIntegerField("know it's unnecessary")
offer_data = models.TextField('offer data dict here')
class Feedback(models.Model):
offer_id = models.PositiveIntegerField()
sent_by = models.ForeignKey(User, verbose_name="Offer sender")
received_by = models.ForeignKey(User, verbose_name="Offer receiver")
def get_offer(self):
try:
Offer.objects.get(id=self.offer_id)
except Offer.DoesNotExist:
return None # offer moved to archive
In first draft there was a offer = models.ForeignKey(Offer) instead of offer_id field,
but I am going to move some old offers from Offer table to another one for archiving.
I would like the feedback stay even if I 'archive' the offer. In feedback list there will be an 'Offer id" link and for offers older than 60 days user will see "moved to archive" when clicking "details".
All I can think of at the moment is getting offers which hasn't expired, but there was a buyer.
ended() is a manager returning self.filter(end_date__isnull=False)
offers_with_buyer = models.Q(Offer.objects.ended().filter(whose__exact=request.user, order__isnull=False) | models.Q(Offer.objects.ended().filter(order__who__exact=request.user)
How do I check if there's a feedback for these offers ?
I know I should return user and offer id from queryset above and check if they exist in Feedback.offer_id and Feedback.sent_by.. or maybe I should change model design completely ...
First, how you're handling the end date is very contrived. If the offer ends 5 days after it's created, then just set that automatically:
from datetime import datetime, timedelta
class Offer(models.Model):
...
def save(self, *args, **kwargs):
self.end_date = datetime.now() + timedelta(days=5)
super(Offer, self).save(*args, **kwargs)
Then, just modify your ended manager to return instead: self.filter(end_date__lte=datetime.now())
However, I generally prefer to add additional methods to my default manager to deal with the data in various ways:
class OfferQuerySet(models.query.QuerySet):
def live(self):
return self.filter(end_date__gt=datetime.now())
def ended(self):
return self.filter(end_date__lte=datetime.now())
class OfferManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
return OffersQuerySet(self.model)
def live(self, *args, **kwargs):
return self.get_query_set().live(*args, **kwargs)
def ended(self, *args, **kwargs):
return self.get_query_set().ended(*args, **kwargs)
(Defining a custom QuerySet and then using methods on the Manager that just proxy to the QuerySet is the way the Django core team does it, and allows you to use the methods anywhere in the chain, instead of just the front.)
As far as "archiving" your Offers go, it's extremely bad practice to divvy out similar data into two different models/tables. It's exponentially increases the order of complexity in your app. If you want to "archive" an offer. Add a field such as:
is_archived = models.BooleanField(default=False)
You can then create another method in your Manager and QuerySet subclasses to filter out just archived or live offers:
def live(self):
return self.filter(is_archived=False, end_date__gt=datetime.now())
def archived(self):
return self.filter(is_archived=True)
If you really need another model (such as for a separate view in the admin for just archived offers) you can create a proxy model:
class ArchivedOfferManager(models.Manager):
def get_query_set(self):
return super(ArchivedOfferManager, self).get_query_set().filter(is_archived=True)
def create(self, **kwargs):
kwargs['is_archived'] = True
return super(ArchivedOfferManager, self).create(**kwargs)
class ArchivedOffer(models.Model)
class Meta:
proxy = True
objects = ArchivedOfferManager()
def save(self, *args, **kwargs):
self.is_archived = True
super(ArchivedOffer, self).save(*args, **kwargs)