Is it possible to add an argument to a property in aiogram? - python-3.x

I'm making a multi-lingual functionality for a aiogram bot, it's pretty simple at the moment:
class Localization:
locales = {
'en': {
'command': {
'start': 'Hello!'
}
}
}
def __init__(self):
self.__user_lang = dict()
#property
def available_langs(self):
return tuple(self.locales.keys())
def text(self, id):
return self.locales[self.user_lang(id)]
def user_lang(self, id):
return self.__user_lang.get(id, 'en')
def set_user_lang(self, id, lang):
if lang in self.available_langs:
self.__user_lang[id] = lang
I have to call it like this:
#dp.message_handler(commands=['start'])
async def start_command(message: types.Message):
id = message.from_user.id
await message.answer(locale.text(id)['commands']['start'])
I want to change text in a #property to shorten the entry.
#property
def text(self):
return self.locales[self.user_lang(id)]
This will also allow me to make constructs like this:
#dp.message_handler(lambda m: m.text == locale.text['buttons']['about'])
async def about(message: types.Message):
pass
Not like this:
#dp.message_handler(lambda m: m.text == locale.text(m.from_user.id)['buttons']['about'])
async def about(message: types.Message):
pass
But I absolutely cannot think of how to specify the property for which particular user to get localization.
P.S. I know about i18n, but I'm learning and want to try to write everything myself.

Ok, i'm definitely stupid.
I could just get the current user in any function that is called from the handler:
from aiogram import types
#property
def user_lang(self):
user = types.User.get_current()
return self.__user_lang.get(user.id, 'ru')
And so on...

Related

DRF ViewSet extra action (`#action`) serializer_class

When I try to use Django Rest Framework extra actions on a viewset, I can not make the decorator's serializer_class work.
class ClientViewSet(ModelViewSet):
queryset = Client.objects.all()
serializer_class = ClientSerializer
def get_queryset(self):
# Do things
def get_serializer_class(self):
if self.action in ["create"]:
return CreateClientSerializer
elif self.action in ["retrieve"]:
return ClientDetailSerializer
return self.serializer_class
#action(detail=True, methods=["get"], serializer_class=ClientDetailSerializer)
def get_by_name(self, request, name=None):
"""
Get one Client searching by name.
#param request:
#param name: Client code
#return: Response
"""
queryset = get_object_or_404(Client, name__iexact=name)
serializer = self.get_serializer(queryset)
return Response(serializer.data)
So, even if the extra action is supposedly overriding the ViewSet default serializer class, I still get ClientSerializer instead of ClientDetailSerializer.
The official documentation states that...
The decorator allows you to override any viewset-level configuration such as permission_classes, serializer_class, filter_backends...:
My get_serializer_class override defaults to the ViewSet serializer_class attribute for my extra actions. If I understand correctly, this is basically what GenericAPIView get_serializer_class does under the hood:
def get_serializer_class(self):
"""
(...)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
I guess I'm missing something obvious here. Just can not figure out what...
Any help is appreciated. Thanks in advance :)
Why not use it like this? I'm guessing you're doing something wrong in get_serializer_class.
#action(detail=True, methods=["get"], serializer_class=ClientDetailSerializer)
def get_by_name(self, request, name=None):
"""
Get one Client searching by name.
#param request:
#param name: Client code
#return: Response
"""
object = get_object_or_404(Client, name__iexact=name)
serializer = ClientDetailSerializer(object)
return Response(serializer.data)
When you override the get_serializer_class without calling the super of this class, the super class doesn't run.
user this:
def get_serializer_class(self):
if self.action in ["create"]:
return CreateClientSerializer
elif self.action in ["retrieve"]:
return ClientDetailSerializer
return super().get_serializer_class()

How to add pagination : super()

I am trying to add pagination using super().list() method in modelviewset
class RecentlyViewedVideosViewSet(ResponseViewMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = RecentlyViewedVideos.objects.all()
serializer_class = RecentlyViewedVideosSerializer
def list(self, request, **kwargs):
print('list')
try:
if 'learner_id' in self.kwargs:
learner_id = self.kwargs['learner_id']
else:
learner_id = self.request.learner.id
response_data = super().get_queryset().filter(learner_id=learner_id)
print(response_data)
serializer = RecentlyViewedVideosSerializer(response_data, many=True)
return self.jp_response(s_code='HTTP_200_OK', data=serializer.data)
in output, it displays all the documents in the table, but I only need those details in the "details", give me a way to get the exact output.
It might be better to do this in the .get_queryset(…) method:
def get_queryset(self):
if 'learner_id' in self.kwargs:
learner_id = self.kwargs['learner_id']
else:
learner_id = self.request.learner.id
return super().get_queryset().filter(learner_id=learner_id)
then the boilerplate code to filter, paginate, etc. the view are still implemented by the .list(…) method of the ListModelMixin.

Decorator arguments lost in self

I have a decorator
class Utilities(commands.Cog):
def has_any_role(self, *items):
async def predicate(ctx):
ctx.command.needed_roles = items
return await commands.has_any_role(*items).predicate(ctx)
return commands.check(predicate)
#has_any_role('Owner', 'Moderator')
#commands.command()
async def foo(...):
...
However now when I try to access a Commands needed_roles attribute, it'll only return Moderator for foo, since "Owner" is lost in self. How can I fix this without just putting the function outside of the class?
Try:
#staticmethod
def has_any_role(*items)
...

How to set parameter for all of the methods in Inherited class

I am currently learning Object Oriented Programming in Python. I have some issues with understanding how to pass to all methods that I inherite from Parten class. I will show you example of code.
Below you can see my Parent class (it's simple api wrapper).
class AlphaVantageClient:
_URL = "https://www.alphavantage.co/query?"
def __init__(self, api_key=None):
self.__api_key = api_key
def show_base_url(self):
print(self._URL)
def set_api_key(self, api_key: str):
self.__api_key = api_key
def __call_api(self, query_parameters: dict,**kwargs):
"""
The structure looks like:
co./query?function{}&symbol{}
:param query_parameters:
:return: response
"""
query_parameters["apikey"] = self.__api_key
response = requests.get(
AlphaVantageClient._URL,
params=query_parameters,
proxies=self.__proxy
)
validate_http_status(response)
return response.json()
def company_overview(self, ticker: str, **kwargs):
query_parameters = {
"function": "OVERVIEW",
"symbol": ticker
}
return self.__call_api(query_parameters, **kwargs)
def balance_sheet(self, ticker: str, **kwargs):
query_parameters = {
"function": "BALANCE_SHEET",
"symbol": ticker
}
return self.__call_api(query_parameters, **kwargs)
def income_statement(self, ticker: str, **kwargs):
query_parameters = {
"function": "INCOME_STATEMENT",
"symbol": ticker
}
return self.__call_api(query_parameters, **kwargs)
def cash_flow(self, ticker: str, **kwargs):
query_parameters = {
"function": "CASH_FLOW",
"symbol": ticker
}
return self.__call_api(query_parameters, **kwargs)
And below there is my child class in which I want to inheritate from AlphaVantageClient class all of the methods.
class Stock(AlphaVantageClient):
def __init__(self, ticker: str):
super().__init__()
self.ticker = ticker
self.client = AlphaVantageClient()
def set_api_key(self, api_key):
self.client.set_api_key(api_key)
But my problem is that as you see in AlphaVantegeClient all of my methods has parameter symbol, I would like to set in one line or something like that for all of the methods to have my symbol parameter set to self.ticker from Stock class. Is it possible to do that ? Or I need to somehow refactor Parent class code ?
You misunderstand how inheritance works. When you create a class Stock that subclasses (inherits from) class AlphaVantageClient it automatically gets all of the attributes and methods of that parent class as if you defined them as part of the new subclass.
You should absolutely not have this line in the init: self.client = AlphaVantageClient(). By subclassing (inheriting) from AlphaVantageClient it already is an instance of AlphaVantageClient and that creates a separate additional instance of and AlphaVantageClient separate from the one it is.
In your Stock class you do not have to redefine set_api_key() unless you are trying to change it for some reason and you are not. This method is just trying to save the api_key into an internal class attribute and it already does that so just leave it out/un-redefined in Stock.
You do not need to rework AlphaVantageClient, but you do need to have stubs in Stock since they need to substitute in the saved ticker parameter into the calls to the parent. Like this:
class Stock(AlphaVantageClient):
def __init__(self, ticker: str, api_key=None):
super().__init__(api_key)
self.ticker = ticker
def company_overview(self, **kwargs):
return super().company_overview(self.ticker, **kwargs)
def balance_sheet(self, **kwargs):
return super().balance_sheet(self.ticker, **kwargs)
def income_statement(self, **kwargs):
return super().income_statement(self.ticker, **kwargs)
def cash_flow(self, **kwargs):
return super().cash_flow(self.ticker, **kwargs)
Thank you for you answer. I don't know if I get it correctly.
class AlphaVantageClient:
_URL = "https://www.alphavantage.co/query?"
def __init__(self, api_key=None, symbol=None):
self.__api_key = api_key
self.__proxy = {}
self.__validate_api_key()
self.__data_type = 'json'
self.__symbol = symbol
def set_api_key(self, api_key: str):
self.__api_key = api_key
def __call_api(self, query_parameters: dict,**kwargs):
"""
The structure looks like:
co./query?function{}&symbol{}
:param query_parameters:
:return: response
"""
query_parameters["apikey"] = self.__api_key
response = requests.get(
AlphaVantageClient._URL,
params=query_parameters,
proxies=self.__proxy
)
validate_http_status(response)
return response.json()
def company_overview(self, symbol: str, **kwargs):
query_parameters = {
"function": "OVERVIEW",
"symbol": symbol
}
return self.__call_api(query_parameters, **kwargs)
def balance_sheet(self, symbol: str, **kwargs):
query_parameters = {
"function": "BALANCE_SHEET",
"symbol": symbol
}
return self.__call_api(query_parameters, **kwargs)
def income_statement(self, symbol: str, **kwargs):
query_parameters = {
"function": "INCOME_STATEMENT",
"symbol": symbol
}
return self.__call_api(query_parameters, **kwargs)
def cash_flow(self, symbol: str, **kwargs):
query_parameters = {
"function": "CASH_FLOW",
"symbol": symbol
}
return self.__call_api(query_parameters, **kwargs)
class Stock(AlphaVantageClient):
def __init__(self, symbol: str):
super().__init__(symbol)
self.symbol = symbol
self.client = AlphaVantageClient(symbol)
def set_api_key(self, api_key):
self.client.set_api_key(api_key)
Now my classes look like this. And I try to create instance of my class and call one of methods like below:
from client import AlphaVantageClient, Stock
stock = Stock(symbol="FB")
stock.income_statement()
And I get an error:
tests\test_client.py:5: in <module>
stock.income_statement()
E TypeError: income_statement() missing 1 required positional argument: 'symbol'
So I don't know if I don't understand you correctly or I just explained my problem in wrong way. So I will try once more:
If I call my AlphaVantageClient, I want to use it in the way that I create instance of that class and I call methods providing as an parameter symbol of stock e.g
client = AlphaVantageClient()
income_statement = client.income_statement(symbol="FB")
But if I create a Stock class I would like to have symbol stored in class and don't need to provide it as a paramtere in my methods: All the methods should use that symbol that I provided when I created Stock class as default. So the usage should look like below:
stock = Stock(symbol="FB")
income_statement = stock.income_statement()
And this way I will fetch the data for "FB" in all of my methods.
Is it possible ? If yes, can you explain me little more how to achive it.

How to properly inherit class method

I have a database connection class that creates a connection pool. Now as the application grows and I'm adding different types of database writers, I want to move database connections to a separate class and inherit from it. So far I have this:
class ServiceDB:
#classmethod
async def init(cls, settings):
self = ServiceDB()
self.pool = await asyncpg.create_pool(
database=settings["POSTGRES_DB"],
user=settings["POSTGRES_USER"],
password=settings["POSTGRES_PASSWORD"],
host=settings["DB_HOST"],
port=settings["DB_PORT"],
)
return self
class ChildWriter(ServiceDB):
async def write_db(self, query):
# Write to specific table
pass
if __name__ == "__main__":
settings = {'info': "some connection settings"}
query = "SELECT * FROM 'table'"
connection = await ChildWriter().init(settings)
await connection.write_db(msg, query)
When I run this I get AttributeError: 'ServiceDB' object has no attribute 'write_db'. How do I properly extend ServiceDB with the write_db method?
Classmethods receive the "current class" as the first argument. Instantiate this cls, not the fixed baseclass.
class ServiceDB:
#classmethod
async def init(cls, settings):
self = cls() # cls is the *current* class, not just ServiceDB
self.pool = await asyncpg.create_pool(
database=settings["POSTGRES_DB"],
user=settings["POSTGRES_USER"],
password=settings["POSTGRES_PASSWORD"],
host=settings["DB_HOST"],
port=settings["DB_PORT"],
)
return self
Note that ideally, all attributes are set via __init__ instead of a separate classmethod constructor. The separate constructor should just pass on any attributes constructed externally.
class ServiceDB:
def __init__(self, pool):
self.pool = pool
#classmethod
async def init(cls, settings, **kwargs):
pool = await asyncpg.create_pool(
database=settings["POSTGRES_DB"],
user=settings["POSTGRES_USER"],
password=settings["POSTGRES_PASSWORD"],
host=settings["DB_HOST"],
port=settings["DB_PORT"],
)
return cls(pool=pool, **kwargs)
class ChildWriter(ServiceDB):
async def write_db(self, query): ...
if __name__ == "__main__":
settings = {'info': "some connection settings"}
query = "SELECT * FROM 'table'"
# call classmethod on class V
connection = await ChildWriter.init(settings)
await connection.write_db(msg, query)

Resources