How can you create an admin interface that looks like a table? - python

I have a model where each object is conceptually a cell of a table - two ForeignKeys, F and G determine its position on the table (i.e., each tuple (f,g) is linked to one cell). That is, something like that:
class Cell(models.Model):
f = models.ForeignKey(F)
g = models.ForeignKey(G)
value = models.IntegerField()
When I create an admin page for that model using:
admin.site.register(Cell)
I get a page where each cell must be entered by linking to a foo, a bar and setting its val individually. For huge cells, that becomes cumbersome. It would be much more convenient to edit that information like so:
Where F_ and G_ are objects of the F and G models, respectively.
Is it possible to get this interface on Django?

Take a look at django-tables2. I haven't used it but read the docs whilst looking for other functionality and I think it might help you.
Also remembered this video which explains how you can make django work like a spreadsheet which may also be usful to you.
https://www.youtube.com/watch?v=jORuhbJq79M

Related

Django-Import-Export assign Field to specific column

I have a working form which exports all data to xls using Resource and Fields from the django-import-export package. But now I need to go a bit further and make it a bit more advanced. I need to export all data to an existing Excel template, assigning each field to a specific column, as in, for instance: Field 1 needs to be filled in B3, Field 2 in C4 etc. etc.
I'm thinking after_export() can be used here but the docs dont go into detail how to use this method. Does anyone have some experience with this package?
I'm not using the admin integration.
after_export() is a method you can override to modify data before it is returned.
It does nothing by default. However, it supplies the data object, which is a Dataset instance. If you access the dict property, then you will have a list of OrderedDict instances corresponding to each row which is returned by your queryset. Therefore, you can construct an entirely new Dataset (list of dicts) which match up with the format you need.
For example, if you wanted the first column of the first row to appear in cell B3, you could do it like this:
def after_export(self, queryset, data, *args, **kwargs):
newdata = tablib.Dataset()
cell1_val = data.dict[0].get("col1", "")
for i, row in enumerate(data):
if i == 1:
newrow = list(row)
# cell b3
newrow[2] = cell1_val
row = newrow
newdata.append(row)
data.dict = newdata.dict
However, you'll note that this approach is not easy to read and is quite unwieldy. Therefore if you don't need any other features of django-import-export then using a library like openpyxl is probably a better approach.

How to dynamically (i.e through program) change name of a Data dictionary to a dynamically obtained vaiable

I am using python 3.9.6 in Windows 10.
Similar earlier questions at
(1) Creating a dynamic dictionary name
and
(2) How to obtain assembly name dynamically
were different and do not solve my problem.
My data dictionary(dynamically created):
pcm30={'ABB': '-0.92', 'ZYDUSWELL': 2.05}
Dynamically obtained new dictionary name "pCh0109" is in variable z
I have to create different dictionaries to create a data frame.
Now I want to dynamically (i.e through programming) change the name of the dictionary from
'pcm30'
to
'pCh0109'.
The digits in the new name of the dictionary ('pCh0109') indicate the time of creation of the particular dictionary.
How to do it?
Will be grateful for assistance and help.
I would strongly recommend you don't try this unless you absolutely have to, but here's the simplest approach to do that:
pcm30 = {'ABB': '-0.92', 'ZYDUSWELL': 2.05}
globals()['pCh0109'] = globals().pop('pcm30')
# Your IDE might glare at you here, but it'll work out without errors at runtime
print(pCh0109)
Instead I suggest to try this approach - use a dictionary if possible. This will turn out much safer for all. Example below:
def my_func():
d = {}
pcm30 = {'ABB': '-0.92', 'ZYDUSWELL': 2.05}
d['pCh0109'] = locals().pop('pcm30')
print(d['pCh0109']['ABB'])
# -0.92

using input as both an array and a string

I feel like there is a simple solution to this but I am kinda new.
stat_input= input("Hello Mr. Jenner, what are you interested in tracking today?")
I use an input like this which later is used to call upon data and uses that data to calculate statistics and produce histogram charts / normal distributions.
It works quite nicely. Here are some examples where it is used.
cur.execute('SELECT {} FROM statdata'.format(stat_input))
np.array(stat_input).astype(np.float)
sigma = math.sqrt(np.var(stat_input))
So if I type threemonthdata it will pull the array of data from my database and use it . Its great. However, I have one small problem
I understand that threemonthdata refers to an array. Since I am creating charts, I want to use the input as the title so the chart title identifies what data I am drawing and using (as a reference in the future)
ax.set_title('stat_input')
This doesn't work
ax.set_title(' + stat_input + ')
Nor does this. I want the title to say Threemonthdata. But if I input twomonthdata I want it to say twomonthdata and not give me the array of numbers.
Any ideas?
I have never played with psycopg's cursor class. But, from what I can read, it appears that this one does the job for you of turning your string in place into a list whose name is the same as the referring string.
Thus what about defining another viariable to store the string before it is overriden ? As follows
stat_input_title = stat_input.capitalize()
cur.execute('SELECT {} FROM statdata'.format(stat_input))
Henceforth, stat_input_title and stat_input can be used together withouh conflicting.
ax.set_title(stat_input_title)
It looks like the issue you are facing is that you are passing the set_title() a string 'stat_input', and not the variable stat_input. You likely simply need to use:
ax.set_title(stat_input)

Define context variables in behave python

Sometimes, you need to define values dynamically, (like datetime now, random strings, random integers, file contents, etc.) and use them across different steps without being explicit or hard-coding the value.
So, my question is how could I define variables inside of steps (the correct way to do it) to use these variables in the following steps.
Some example
Given A random string of length "100" as "my_text"
And I log in to my platform
And I ask to add the following post:
| title | description |
| Some example of title | {{my_text}} |
When I submit the post form
Then The posts table shows these posts:
| title | description |
| Some example of title | {{my_text}} |
And I delete any post containing in the description "{{my_text}}"
This is a basic example trying to explain why I would like to define variables in steps and save them in the context to use it in the following steps.
My idea was to modify before_step and after_step methods... to set a variable in context to store my custom variables like this:
def before_step(context):
if not hasattr(context, 'vars'):
context.vars = {}
if hasattr(context, table) and context.table:
parse_table(context)
def parse_table(context):
# Here use a regex to check each cell and look for `"{{<identifier>}}"` and if match, replace the cell value by context.vars[identifier] so the step "the posts table shows these posts will never know what is `{{my_text}}` it will be abstract seeing the random string.
Scenarios Outline, use something like this defining variables like "<some_identifier>" and then for each example replace the value in the step.
It's basically to reproduce the behaviour but for any kind of step, simple or using tables.
Is it the right way to do something like this?
From Behave docs on the context:
When behave launches into a new feature or scenario it adds a new layer to the context, allowing the new activity level to add new values, or overwrite ones previously defined, for the duration of that activity. These can be thought of as scopes:
#given('I request a new widget for an account via SOAP')
def step_impl(context):
client = Client("http://127.0.0.1:8000/soap/")
// method client.Allocate(...) returns a dict
context.response = client.Allocate(customer_first='Firstname',
customer_last='Lastname', colour='red')
// context vars can be set more directly
context.new_var = "My new variable!"
#then('I should receive an OK SOAP response')
def step_impl(context):
eq_(context.response['ok'], 1)
cnv = str(context.new_var)
print (f"This is my new variable:'{cnv}'"
So, the value can be set using dot notation and retrieved the same.
To answer this question, one needs note:
Does the test data needs to be controlled externally? For example, test data can be inputed from command line so that the value can be chosen explicitly.
If the answer is no, then probably we should not bother hard coding anything in the feature file. And we can leave the data generated in one step, save it in context, and accessed again in any followed step.
The example I can think is exactly like what the question described. Do we care what the random text content we generated, posted and verified? Probably not. Then we should not expose such detail to user (i.e. feature file) since it is not important to the behaviour we are testing.
If the answer is yes, we do need a bit hack to make it happen. I am experiencing a case like this. What I want is to change the test data when I run the test so I don't have to hard code them in the feature files as in a table or scenario outline. How can I do this?
I can use -D option in command line to pass in as many user data as possible, which can then be accessed in context.config.userdata dictionary in any steps. If the number of test data is very limited. This approach is an easy way to go. But if the test data set contains many data that no one want type one by one in command line, it can be stored externally, for example, a ini file with section names like testdata_1...testdata_n, and thus a string can be passed in from command line to be used to address the section name in this config file. And the test data can be read out in either before_all, or before_scenario, etc., and get used in all steps.
In my experience , you cannot create a dynamic value in feature file.
for example, this step :
Given A random string of length "100" as "my_text"
I dont see any way to change {my_text} each time you run the scenario. (not consider to use behave -D to parse the value to context.config.userdata,I think it is also a wrong approach)
Even Scenario Outline, it actually splits to many scenarios. each scenario will have
different value but the value of {my_text} is already defined in Examples table for each scenario.
The way makes a step dynamic is using Step definition (Code layer).
You can generate a random number in step definition #given('A random string of length "100" as "{my_text}"')
And use context.my_text to store the created number and using it arround.
I also agree with Murphy Meng that you don't need to expose the generated random number
explicitly in feature file. You know which step will use that number, simply use context.my_text in that step to get the value. That's it.

Decorate data before passing them to templates in Django

I hava this kind of confusition when passing data from views to templates. The data is stored in float like this 0.12, and it was displayed like this "12%", so we have to change it like we did below, but here comes the question, what kind of choices shall we choose?
There are two kind of choices, first one is like this:
item_list = Items.objects.all()
for item in item_list:
item.interest_rate *= 100
item.financing_amount /= 10000
...
return render_to_response("xxxx.html",{'ret': item_list})
And the second one is like this:
item_list = Items.objects.all()
ret = []
for item in item_list:
temp = {
"temp_interest_rate": item.interest_rate *= 100,
"temp_interest_rate": item.financing_amount /= 10000,
}
ret.append(item)
...
return render_to_response("xxxx.html",{'ret': ret})
The first one is obviously much more simple, but I think maybe its not a good way to change the database model directly even we did not save it.
I have already convinced my classmate to use the second one for safety, but I haven't convinced myself for that. So is there any experienced Django developer can offer some advice?
First, "0.12" is a string, its not a float.
It is perfectly fine to decorate your objects in your view before passing them to the template; adding properties to objects dynamically does not affect the database.
The problem here is that you are modifying a display detail in the view. This is not ideal. You should let the presentation layer (the template), decide how something is displayed and your view to send across the "raw" data.
The easiest way to resolve this is to create your own template filter, which will format your data correctly at the presentation layer. You'll note that the large library of default filters all serve the same purpose - to convert data into a format for the presentation layer; which is why the recommended approach would be to create your own filter.

Categories

Resources