Inline answers follow... On 23 October 2016 at 10:01, Andrew <[email protected]> wrote: > Tried to look up service layers, but couldn't find examples of how it was > supposed to look like - is it supposed to be like a class, and I toss the > lead into it and it handles business logic and saving?
Keep in mind we're talking about Python, OK? Then, you should not create a class if you don't need it. Simple functions work nice many times. A service layer is just another layer. Your view and template is on the "web" layer. A service layer would be on the "business" layer, between your user interface and your domain model. No secret in it. Just another class or function in another package or module. See this overview for clarification: http://martinfowler.com/eaaCatalog/serviceLayer.html There's no "the right way" to do it. No silver bullet. Design varies from person to person, but I strongly advocate for thin views according to the Single Responsibility Principle, i.e, letting views gather (call) workers (the service layer) to do the "dirty work" and return a response to user. Nothing more. > > Normally I'd use ipinfo.result myself, but I read somewhere about separating > the interface from the internals of the class, so I don't have classes > everywhere depending on this variable having this name in IPInfo. Interfaces are a conceptual stuff. Python doesn't have a word or structure for them. No additional complexity. You can emulate interfaces creating a class implementing public methods (names don't start with an underscore) and simply raising NotImplementedError. It's children should implement these real methods. Some example (Python 3): class MyInterface: def do_something(self): raise NotImplementedError("Must be implemented in child class") class MyClass(MyInterface): def __init__(self): # do some initialisation def do_something(self): # real implementation def _some_private_method(self): # do something You see good examples of "programming to an interface" concept when you read about magic methods in Python docs. These "interfaces" are expected to make your own comparisons, evaluating True/False or object length and so on. See docs here: https://docs.python.org/3/reference/datamodel.html#special-method-names -- Cheers. Vinicius Assef > > Or perhaps that doesn't apply in Python? > > I'm probably really confused... > > On 23 October 2016 at 03:02, Vinicius Assef <[email protected]> wrote: >> >> I'd say you're missing a service layer. >> >> It's known as a good practice to not have "business rules" in your views. >> >> IP lookup should be done in your view anyway, because it's a "web >> related thing". The `save_the_lead()` and email sending should be in >> the service layer. >> >> Extending a little bit and talking about pythonic code, >> `IPInfo.get_result()` should not exist. Simply use the `result` >> attribute or convert it to a read only property. Or, if this class >> came to life just because the `get_result()` method, convert it to a >> simple function. >> >> >> >> >> On 21 October 2016 at 09:11, Andrew Chiw <[email protected]> >> wrote: >> > In my views, I have this: >> > def questionnaire(request): >> > def save_the_lead(cleaned_data, ipinfo): >> > email = cleaned_data.pop('email', None) >> > lead, created = Lead.objects.update_or_create(email=email) >> > lead.q = cleaned_data >> > lead.ipinfo = ipinfo >> > lead.save() >> > return lead >> > >> > form = LeadForm(request.POST or request.GET or None) >> > """ >> > Why am I still checking for request.method == "POST"? >> > if POST (valid data) to questionnaire, process it. >> > if GET (valid data) to questionnaire, display it first. Even if it's >> > all >> > valid. >> > """ >> > if request.method == "POST": >> > if form.is_valid(): >> > i = IPInfo(get_ip(request), dummy=True) >> > lead = save_the_lead(form.cleaned_data, i.get_result()) >> > m = Matcher() >> > m.match([lead]) >> > e = Emailer(lead) >> > e.send() >> > return redirect('results', lead.id) >> > >> > return render(request, 'wizard/questionnaire.html', {'form': form}) >> > >> > And IPInfo looks like this: >> > import requests >> > import ipdb >> > >> > >> > class IPInfo: >> > def __init__(self, ip, dummy=False): >> > self.result = dict() >> > if (ip and not dummy): >> > response = requests.get('http://ipinfo.io/' + ip) >> > self.result = response.json() >> > elif (ip and dummy): >> > self.result = {"ip": ip} >> > >> > def get_result(self): >> > return self.result >> > >> > >> > The part I'm worried about is the IPInfo lookup class and how it is used >> > in >> > the view. I know you can mock classes in tests, but maybe I should use >> > dependency injection instead? or is that not needed in Python? >> > If I'm missing dependency injection: I don't have control over the code >> > that >> > runs before the view, so I cannot tell it to inject another instance of >> > IPInfo in there. >> > >> > -- >> > You received this message because you are subscribed to the Google >> > Groups >> > "Django users" group. >> > To unsubscribe from this group and stop receiving emails from it, send >> > an >> > email to [email protected]. >> > To post to this group, send email to [email protected]. >> > Visit this group at https://groups.google.com/group/django-users. >> > To view this discussion on the web visit >> > >> > https://groups.google.com/d/msgid/django-users/2777f9b7-ec87-4dc5-ac8f-de971860c83b%40googlegroups.com. >> > For more options, visit https://groups.google.com/d/optout. >> >> -- >> You received this message because you are subscribed to a topic in the >> Google Groups "Django users" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/django-users/H_N9ierYHJk/unsubscribe. >> To unsubscribe from this group and all its topics, send an email to >> [email protected]. >> To post to this group, send email to [email protected]. >> Visit this group at https://groups.google.com/group/django-users. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/django-users/CAFmXjSDVUwCE6Tpaiv_HMRvejkrA6XvSu0zWP0tjfSsUsctHZg%40mail.gmail.com. >> For more options, visit https://groups.google.com/d/optout. > > > > > -- > > randomshinichi > > ....pi......pi......kaaaaaaa.... > AMD Thuban 3722MHz, 12288MB DDR3-1466 CL9 SDRAM, Asus M5A99X EVO, nVidia > GTX470@840/3800MHz+Gelid Icy Vision Rev. 2 > > -- > You received this message because you are subscribed to the Google Groups > "Django users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To post to this group, send email to [email protected]. > Visit this group at https://groups.google.com/group/django-users. > To view this discussion on the web visit > https://groups.google.com/d/msgid/django-users/CAEam--U9vTBE1xsvVxMHXPjx_z%2B69bJcUBXFjNEeF7iLD%2B3Xcg%40mail.gmail.com. > > For more options, visit https://groups.google.com/d/optout. -- You received this message because you are subscribed to the Google Groups "Django users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at https://groups.google.com/group/django-users. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAFmXjSCSdfj7bdNw0_Kpv_Tsxsxko-NJ-iW27y%3D5K_ge4HRxZA%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.

